roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls)
8864         {
8865             if(this.getStyle("position") == "static"){
8866                 this.setStyle("position", "relative");
8867             }
8868             if(!this._mask){
8869                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8870             }
8871             this.addClass("x-masked");
8872             this._mask.setDisplayed(true);
8873             
8874             // we wander
8875             var z = 0;
8876             var dom = this.dom
8877             while (dom && dom.style) {
8878                 if (!isNaN(parseInt(dom.style.zIndex))) {
8879                     z = Math.max(z, parseInt(dom.style.zIndex));
8880                 }
8881                 dom = dom.parentNode;
8882             }
8883             
8884            
8885             if(typeof msg == 'string'){
8886                 if(!this._maskMsg){
8887                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8888                 }
8889                 var mm = this._maskMsg;
8890                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8891                 mm.dom.firstChild.innerHTML = msg;
8892                 mm.setDisplayed(true);
8893                 mm.center(this);
8894                 mm.setStyle('z-index', z + 102);
8895             }
8896             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8897                 this._mask.setHeight(this.getHeight());
8898             }
8899             this._mask.setStyle('z-index', z + 100);
8900             
8901             return this._mask;
8902         },
8903
8904         /**
8905          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8906          * it is cached for reuse.
8907          */
8908         unmask : function(removeEl){
8909             if(this._mask){
8910                 if(removeEl === true){
8911                     this._mask.remove();
8912                     delete this._mask;
8913                     if(this._maskMsg){
8914                         this._maskMsg.remove();
8915                         delete this._maskMsg;
8916                     }
8917                 }else{
8918                     this._mask.setDisplayed(false);
8919                     if(this._maskMsg){
8920                         this._maskMsg.setDisplayed(false);
8921                     }
8922                 }
8923             }
8924             this.removeClass("x-masked");
8925         },
8926
8927         /**
8928          * Returns true if this element is masked
8929          * @return {Boolean}
8930          */
8931         isMasked : function(){
8932             return this._mask && this._mask.isVisible();
8933         },
8934
8935         /**
8936          * Creates an iframe shim for this element to keep selects and other windowed objects from
8937          * showing through.
8938          * @return {Roo.Element} The new shim element
8939          */
8940         createShim : function(){
8941             var el = document.createElement('iframe');
8942             el.frameBorder = 'no';
8943             el.className = 'roo-shim';
8944             if(Roo.isIE && Roo.isSecure){
8945                 el.src = Roo.SSL_SECURE_URL;
8946             }
8947             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8948             shim.autoBoxAdjust = false;
8949             return shim;
8950         },
8951
8952         /**
8953          * Removes this element from the DOM and deletes it from the cache
8954          */
8955         remove : function(){
8956             if(this.dom.parentNode){
8957                 this.dom.parentNode.removeChild(this.dom);
8958             }
8959             delete El.cache[this.dom.id];
8960         },
8961
8962         /**
8963          * Sets up event handlers to add and remove a css class when the mouse is over this element
8964          * @param {String} className
8965          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8966          * mouseout events for children elements
8967          * @return {Roo.Element} this
8968          */
8969         addClassOnOver : function(className, preventFlicker){
8970             this.on("mouseover", function(){
8971                 Roo.fly(this, '_internal').addClass(className);
8972             }, this.dom);
8973             var removeFn = function(e){
8974                 if(preventFlicker !== true || !e.within(this, true)){
8975                     Roo.fly(this, '_internal').removeClass(className);
8976                 }
8977             };
8978             this.on("mouseout", removeFn, this.dom);
8979             return this;
8980         },
8981
8982         /**
8983          * Sets up event handlers to add and remove a css class when this element has the focus
8984          * @param {String} className
8985          * @return {Roo.Element} this
8986          */
8987         addClassOnFocus : function(className){
8988             this.on("focus", function(){
8989                 Roo.fly(this, '_internal').addClass(className);
8990             }, this.dom);
8991             this.on("blur", function(){
8992                 Roo.fly(this, '_internal').removeClass(className);
8993             }, this.dom);
8994             return this;
8995         },
8996         /**
8997          * 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)
8998          * @param {String} className
8999          * @return {Roo.Element} this
9000          */
9001         addClassOnClick : function(className){
9002             var dom = this.dom;
9003             this.on("mousedown", function(){
9004                 Roo.fly(dom, '_internal').addClass(className);
9005                 var d = Roo.get(document);
9006                 var fn = function(){
9007                     Roo.fly(dom, '_internal').removeClass(className);
9008                     d.removeListener("mouseup", fn);
9009                 };
9010                 d.on("mouseup", fn);
9011             });
9012             return this;
9013         },
9014
9015         /**
9016          * Stops the specified event from bubbling and optionally prevents the default action
9017          * @param {String} eventName
9018          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9019          * @return {Roo.Element} this
9020          */
9021         swallowEvent : function(eventName, preventDefault){
9022             var fn = function(e){
9023                 e.stopPropagation();
9024                 if(preventDefault){
9025                     e.preventDefault();
9026                 }
9027             };
9028             if(eventName instanceof Array){
9029                 for(var i = 0, len = eventName.length; i < len; i++){
9030                      this.on(eventName[i], fn);
9031                 }
9032                 return this;
9033             }
9034             this.on(eventName, fn);
9035             return this;
9036         },
9037
9038         /**
9039          * @private
9040          */
9041       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9042
9043         /**
9044          * Sizes this element to its parent element's dimensions performing
9045          * neccessary box adjustments.
9046          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9047          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9048          * @return {Roo.Element} this
9049          */
9050         fitToParent : function(monitorResize, targetParent) {
9051           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9052           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9053           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9054             return;
9055           }
9056           var p = Roo.get(targetParent || this.dom.parentNode);
9057           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9058           if (monitorResize === true) {
9059             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9060             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9061           }
9062           return this;
9063         },
9064
9065         /**
9066          * Gets the next sibling, skipping text nodes
9067          * @return {HTMLElement} The next sibling or null
9068          */
9069         getNextSibling : function(){
9070             var n = this.dom.nextSibling;
9071             while(n && n.nodeType != 1){
9072                 n = n.nextSibling;
9073             }
9074             return n;
9075         },
9076
9077         /**
9078          * Gets the previous sibling, skipping text nodes
9079          * @return {HTMLElement} The previous sibling or null
9080          */
9081         getPrevSibling : function(){
9082             var n = this.dom.previousSibling;
9083             while(n && n.nodeType != 1){
9084                 n = n.previousSibling;
9085             }
9086             return n;
9087         },
9088
9089
9090         /**
9091          * Appends the passed element(s) to this element
9092          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9093          * @return {Roo.Element} this
9094          */
9095         appendChild: function(el){
9096             el = Roo.get(el);
9097             el.appendTo(this);
9098             return this;
9099         },
9100
9101         /**
9102          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9103          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9104          * automatically generated with the specified attributes.
9105          * @param {HTMLElement} insertBefore (optional) a child element of this element
9106          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9107          * @return {Roo.Element} The new child element
9108          */
9109         createChild: function(config, insertBefore, returnDom){
9110             config = config || {tag:'div'};
9111             if(insertBefore){
9112                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9113             }
9114             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9115         },
9116
9117         /**
9118          * Appends this element to the passed element
9119          * @param {String/HTMLElement/Element} el The new parent element
9120          * @return {Roo.Element} this
9121          */
9122         appendTo: function(el){
9123             el = Roo.getDom(el);
9124             el.appendChild(this.dom);
9125             return this;
9126         },
9127
9128         /**
9129          * Inserts this element before the passed element in the DOM
9130          * @param {String/HTMLElement/Element} el The element to insert before
9131          * @return {Roo.Element} this
9132          */
9133         insertBefore: function(el){
9134             el = Roo.getDom(el);
9135             el.parentNode.insertBefore(this.dom, el);
9136             return this;
9137         },
9138
9139         /**
9140          * Inserts this element after the passed element in the DOM
9141          * @param {String/HTMLElement/Element} el The element to insert after
9142          * @return {Roo.Element} this
9143          */
9144         insertAfter: function(el){
9145             el = Roo.getDom(el);
9146             el.parentNode.insertBefore(this.dom, el.nextSibling);
9147             return this;
9148         },
9149
9150         /**
9151          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @return {Roo.Element} The new child
9154          */
9155         insertFirst: function(el, returnDom){
9156             el = el || {};
9157             if(typeof el == 'object' && !el.nodeType){ // dh config
9158                 return this.createChild(el, this.dom.firstChild, returnDom);
9159             }else{
9160                 el = Roo.getDom(el);
9161                 this.dom.insertBefore(el, this.dom.firstChild);
9162                 return !returnDom ? Roo.get(el) : el;
9163             }
9164         },
9165
9166         /**
9167          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9168          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9169          * @param {String} where (optional) 'before' or 'after' defaults to before
9170          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9171          * @return {Roo.Element} the inserted Element
9172          */
9173         insertSibling: function(el, where, returnDom){
9174             where = where ? where.toLowerCase() : 'before';
9175             el = el || {};
9176             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9177
9178             if(typeof el == 'object' && !el.nodeType){ // dh config
9179                 if(where == 'after' && !this.dom.nextSibling){
9180                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9181                 }else{
9182                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9183                 }
9184
9185             }else{
9186                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9187                             where == 'before' ? this.dom : this.dom.nextSibling);
9188                 if(!returnDom){
9189                     rt = Roo.get(rt);
9190                 }
9191             }
9192             return rt;
9193         },
9194
9195         /**
9196          * Creates and wraps this element with another element
9197          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9198          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9199          * @return {HTMLElement/Element} The newly created wrapper element
9200          */
9201         wrap: function(config, returnDom){
9202             if(!config){
9203                 config = {tag: "div"};
9204             }
9205             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9206             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9207             return newEl;
9208         },
9209
9210         /**
9211          * Replaces the passed element with this element
9212          * @param {String/HTMLElement/Element} el The element to replace
9213          * @return {Roo.Element} this
9214          */
9215         replace: function(el){
9216             el = Roo.get(el);
9217             this.insertBefore(el);
9218             el.remove();
9219             return this;
9220         },
9221
9222         /**
9223          * Inserts an html fragment into this element
9224          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9225          * @param {String} html The HTML fragment
9226          * @param {Boolean} returnEl True to return an Roo.Element
9227          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9228          */
9229         insertHtml : function(where, html, returnEl){
9230             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9231             return returnEl ? Roo.get(el) : el;
9232         },
9233
9234         /**
9235          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9236          * @param {Object} o The object with the attributes
9237          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9238          * @return {Roo.Element} this
9239          */
9240         set : function(o, useSet){
9241             var el = this.dom;
9242             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9243             for(var attr in o){
9244                 if(attr == "style" || typeof o[attr] == "function") continue;
9245                 if(attr=="cls"){
9246                     el.className = o["cls"];
9247                 }else{
9248                     if(useSet) el.setAttribute(attr, o[attr]);
9249                     else el[attr] = o[attr];
9250                 }
9251             }
9252             if(o.style){
9253                 Roo.DomHelper.applyStyles(el, o.style);
9254             }
9255             return this;
9256         },
9257
9258         /**
9259          * Convenience method for constructing a KeyMap
9260          * @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:
9261          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9262          * @param {Function} fn The function to call
9263          * @param {Object} scope (optional) The scope of the function
9264          * @return {Roo.KeyMap} The KeyMap created
9265          */
9266         addKeyListener : function(key, fn, scope){
9267             var config;
9268             if(typeof key != "object" || key instanceof Array){
9269                 config = {
9270                     key: key,
9271                     fn: fn,
9272                     scope: scope
9273                 };
9274             }else{
9275                 config = {
9276                     key : key.key,
9277                     shift : key.shift,
9278                     ctrl : key.ctrl,
9279                     alt : key.alt,
9280                     fn: fn,
9281                     scope: scope
9282                 };
9283             }
9284             return new Roo.KeyMap(this, config);
9285         },
9286
9287         /**
9288          * Creates a KeyMap for this element
9289          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9290          * @return {Roo.KeyMap} The KeyMap created
9291          */
9292         addKeyMap : function(config){
9293             return new Roo.KeyMap(this, config);
9294         },
9295
9296         /**
9297          * Returns true if this element is scrollable.
9298          * @return {Boolean}
9299          */
9300          isScrollable : function(){
9301             var dom = this.dom;
9302             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9303         },
9304
9305         /**
9306          * 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().
9307          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9308          * @param {Number} value The new scroll value
9309          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9310          * @return {Element} this
9311          */
9312
9313         scrollTo : function(side, value, animate){
9314             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9315             if(!animate || !A){
9316                 this.dom[prop] = value;
9317             }else{
9318                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9319                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9320             }
9321             return this;
9322         },
9323
9324         /**
9325          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9326          * within this element's scrollable range.
9327          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9328          * @param {Number} distance How far to scroll the element in pixels
9329          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9330          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9331          * was scrolled as far as it could go.
9332          */
9333          scroll : function(direction, distance, animate){
9334              if(!this.isScrollable()){
9335                  return;
9336              }
9337              var el = this.dom;
9338              var l = el.scrollLeft, t = el.scrollTop;
9339              var w = el.scrollWidth, h = el.scrollHeight;
9340              var cw = el.clientWidth, ch = el.clientHeight;
9341              direction = direction.toLowerCase();
9342              var scrolled = false;
9343              var a = this.preanim(arguments, 2);
9344              switch(direction){
9345                  case "l":
9346                  case "left":
9347                      if(w - l > cw){
9348                          var v = Math.min(l + distance, w-cw);
9349                          this.scrollTo("left", v, a);
9350                          scrolled = true;
9351                      }
9352                      break;
9353                 case "r":
9354                 case "right":
9355                      if(l > 0){
9356                          var v = Math.max(l - distance, 0);
9357                          this.scrollTo("left", v, a);
9358                          scrolled = true;
9359                      }
9360                      break;
9361                 case "t":
9362                 case "top":
9363                 case "up":
9364                      if(t > 0){
9365                          var v = Math.max(t - distance, 0);
9366                          this.scrollTo("top", v, a);
9367                          scrolled = true;
9368                      }
9369                      break;
9370                 case "b":
9371                 case "bottom":
9372                 case "down":
9373                      if(h - t > ch){
9374                          var v = Math.min(t + distance, h-ch);
9375                          this.scrollTo("top", v, a);
9376                          scrolled = true;
9377                      }
9378                      break;
9379              }
9380              return scrolled;
9381         },
9382
9383         /**
9384          * Translates the passed page coordinates into left/top css values for this element
9385          * @param {Number/Array} x The page x or an array containing [x, y]
9386          * @param {Number} y The page y
9387          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9388          */
9389         translatePoints : function(x, y){
9390             if(typeof x == 'object' || x instanceof Array){
9391                 y = x[1]; x = x[0];
9392             }
9393             var p = this.getStyle('position');
9394             var o = this.getXY();
9395
9396             var l = parseInt(this.getStyle('left'), 10);
9397             var t = parseInt(this.getStyle('top'), 10);
9398
9399             if(isNaN(l)){
9400                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9401             }
9402             if(isNaN(t)){
9403                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9404             }
9405
9406             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9407         },
9408
9409         /**
9410          * Returns the current scroll position of the element.
9411          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9412          */
9413         getScroll : function(){
9414             var d = this.dom, doc = document;
9415             if(d == doc || d == doc.body){
9416                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9417                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9418                 return {left: l, top: t};
9419             }else{
9420                 return {left: d.scrollLeft, top: d.scrollTop};
9421             }
9422         },
9423
9424         /**
9425          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9426          * are convert to standard 6 digit hex color.
9427          * @param {String} attr The css attribute
9428          * @param {String} defaultValue The default value to use when a valid color isn't found
9429          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9430          * YUI color anims.
9431          */
9432         getColor : function(attr, defaultValue, prefix){
9433             var v = this.getStyle(attr);
9434             if(!v || v == "transparent" || v == "inherit") {
9435                 return defaultValue;
9436             }
9437             var color = typeof prefix == "undefined" ? "#" : prefix;
9438             if(v.substr(0, 4) == "rgb("){
9439                 var rvs = v.slice(4, v.length -1).split(",");
9440                 for(var i = 0; i < 3; i++){
9441                     var h = parseInt(rvs[i]).toString(16);
9442                     if(h < 16){
9443                         h = "0" + h;
9444                     }
9445                     color += h;
9446                 }
9447             } else {
9448                 if(v.substr(0, 1) == "#"){
9449                     if(v.length == 4) {
9450                         for(var i = 1; i < 4; i++){
9451                             var c = v.charAt(i);
9452                             color +=  c + c;
9453                         }
9454                     }else if(v.length == 7){
9455                         color += v.substr(1);
9456                     }
9457                 }
9458             }
9459             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9460         },
9461
9462         /**
9463          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9464          * gradient background, rounded corners and a 4-way shadow.
9465          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9466          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9467          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9468          * @return {Roo.Element} this
9469          */
9470         boxWrap : function(cls){
9471             cls = cls || 'x-box';
9472             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9473             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9474             return el;
9475         },
9476
9477         /**
9478          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9479          * @param {String} namespace The namespace in which to look for the attribute
9480          * @param {String} name The attribute name
9481          * @return {String} The attribute value
9482          */
9483         getAttributeNS : Roo.isIE ? function(ns, name){
9484             var d = this.dom;
9485             var type = typeof d[ns+":"+name];
9486             if(type != 'undefined' && type != 'unknown'){
9487                 return d[ns+":"+name];
9488             }
9489             return d[name];
9490         } : function(ns, name){
9491             var d = this.dom;
9492             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9493         }
9494     };
9495
9496     var ep = El.prototype;
9497
9498     /**
9499      * Appends an event handler (Shorthand for addListener)
9500      * @param {String}   eventName     The type of event to append
9501      * @param {Function} fn        The method the event invokes
9502      * @param {Object} scope       (optional) The scope (this object) of the fn
9503      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9504      * @method
9505      */
9506     ep.on = ep.addListener;
9507         // backwards compat
9508     ep.mon = ep.addListener;
9509
9510     /**
9511      * Removes an event handler from this element (shorthand for removeListener)
9512      * @param {String} eventName the type of event to remove
9513      * @param {Function} fn the method the event invokes
9514      * @return {Roo.Element} this
9515      * @method
9516      */
9517     ep.un = ep.removeListener;
9518
9519     /**
9520      * true to automatically adjust width and height settings for box-model issues (default to true)
9521      */
9522     ep.autoBoxAdjust = true;
9523
9524     // private
9525     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9526
9527     // private
9528     El.addUnits = function(v, defaultUnit){
9529         if(v === "" || v == "auto"){
9530             return v;
9531         }
9532         if(v === undefined){
9533             return '';
9534         }
9535         if(typeof v == "number" || !El.unitPattern.test(v)){
9536             return v + (defaultUnit || 'px');
9537         }
9538         return v;
9539     };
9540
9541     // special markup used throughout Roo when box wrapping elements
9542     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>';
9543     /**
9544      * Visibility mode constant - Use visibility to hide element
9545      * @static
9546      * @type Number
9547      */
9548     El.VISIBILITY = 1;
9549     /**
9550      * Visibility mode constant - Use display to hide element
9551      * @static
9552      * @type Number
9553      */
9554     El.DISPLAY = 2;
9555
9556     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9557     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9558     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9559
9560
9561
9562     /**
9563      * @private
9564      */
9565     El.cache = {};
9566
9567     var docEl;
9568
9569     /**
9570      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9571      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9572      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9573      * @return {Element} The Element object
9574      * @static
9575      */
9576     El.get = function(el){
9577         var ex, elm, id;
9578         if(!el){ return null; }
9579         if(typeof el == "string"){ // element id
9580             if(!(elm = document.getElementById(el))){
9581                 return null;
9582             }
9583             if(ex = El.cache[el]){
9584                 ex.dom = elm;
9585             }else{
9586                 ex = El.cache[el] = new El(elm);
9587             }
9588             return ex;
9589         }else if(el.tagName){ // dom element
9590             if(!(id = el.id)){
9591                 id = Roo.id(el);
9592             }
9593             if(ex = El.cache[id]){
9594                 ex.dom = el;
9595             }else{
9596                 ex = El.cache[id] = new El(el);
9597             }
9598             return ex;
9599         }else if(el instanceof El){
9600             if(el != docEl){
9601                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9602                                                               // catch case where it hasn't been appended
9603                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9604             }
9605             return el;
9606         }else if(el.isComposite){
9607             return el;
9608         }else if(el instanceof Array){
9609             return El.select(el);
9610         }else if(el == document){
9611             // create a bogus element object representing the document object
9612             if(!docEl){
9613                 var f = function(){};
9614                 f.prototype = El.prototype;
9615                 docEl = new f();
9616                 docEl.dom = document;
9617             }
9618             return docEl;
9619         }
9620         return null;
9621     };
9622
9623     // private
9624     El.uncache = function(el){
9625         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9626             if(a[i]){
9627                 delete El.cache[a[i].id || a[i]];
9628             }
9629         }
9630     };
9631
9632     // private
9633     // Garbage collection - uncache elements/purge listeners on orphaned elements
9634     // so we don't hold a reference and cause the browser to retain them
9635     El.garbageCollect = function(){
9636         if(!Roo.enableGarbageCollector){
9637             clearInterval(El.collectorThread);
9638             return;
9639         }
9640         for(var eid in El.cache){
9641             var el = El.cache[eid], d = el.dom;
9642             // -------------------------------------------------------
9643             // Determining what is garbage:
9644             // -------------------------------------------------------
9645             // !d
9646             // dom node is null, definitely garbage
9647             // -------------------------------------------------------
9648             // !d.parentNode
9649             // no parentNode == direct orphan, definitely garbage
9650             // -------------------------------------------------------
9651             // !d.offsetParent && !document.getElementById(eid)
9652             // display none elements have no offsetParent so we will
9653             // also try to look it up by it's id. However, check
9654             // offsetParent first so we don't do unneeded lookups.
9655             // This enables collection of elements that are not orphans
9656             // directly, but somewhere up the line they have an orphan
9657             // parent.
9658             // -------------------------------------------------------
9659             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9660                 delete El.cache[eid];
9661                 if(d && Roo.enableListenerCollection){
9662                     E.purgeElement(d);
9663                 }
9664             }
9665         }
9666     }
9667     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9668
9669
9670     // dom is optional
9671     El.Flyweight = function(dom){
9672         this.dom = dom;
9673     };
9674     El.Flyweight.prototype = El.prototype;
9675
9676     El._flyweights = {};
9677     /**
9678      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9679      * the dom node can be overwritten by other code.
9680      * @param {String/HTMLElement} el The dom node or id
9681      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9682      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9683      * @static
9684      * @return {Element} The shared Element object
9685      */
9686     El.fly = function(el, named){
9687         named = named || '_global';
9688         el = Roo.getDom(el);
9689         if(!el){
9690             return null;
9691         }
9692         if(!El._flyweights[named]){
9693             El._flyweights[named] = new El.Flyweight();
9694         }
9695         El._flyweights[named].dom = el;
9696         return El._flyweights[named];
9697     };
9698
9699     /**
9700      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9701      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9702      * Shorthand of {@link Roo.Element#get}
9703      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9704      * @return {Element} The Element object
9705      * @member Roo
9706      * @method get
9707      */
9708     Roo.get = El.get;
9709     /**
9710      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9711      * the dom node can be overwritten by other code.
9712      * Shorthand of {@link Roo.Element#fly}
9713      * @param {String/HTMLElement} el The dom node or id
9714      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9715      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9716      * @static
9717      * @return {Element} The shared Element object
9718      * @member Roo
9719      * @method fly
9720      */
9721     Roo.fly = El.fly;
9722
9723     // speedy lookup for elements never to box adjust
9724     var noBoxAdjust = Roo.isStrict ? {
9725         select:1
9726     } : {
9727         input:1, select:1, textarea:1
9728     };
9729     if(Roo.isIE || Roo.isGecko){
9730         noBoxAdjust['button'] = 1;
9731     }
9732
9733
9734     Roo.EventManager.on(window, 'unload', function(){
9735         delete El.cache;
9736         delete El._flyweights;
9737     });
9738 })();
9739
9740
9741
9742
9743 if(Roo.DomQuery){
9744     Roo.Element.selectorFunction = Roo.DomQuery.select;
9745 }
9746
9747 Roo.Element.select = function(selector, unique, root){
9748     var els;
9749     if(typeof selector == "string"){
9750         els = Roo.Element.selectorFunction(selector, root);
9751     }else if(selector.length !== undefined){
9752         els = selector;
9753     }else{
9754         throw "Invalid selector";
9755     }
9756     if(unique === true){
9757         return new Roo.CompositeElement(els);
9758     }else{
9759         return new Roo.CompositeElementLite(els);
9760     }
9761 };
9762 /**
9763  * Selects elements based on the passed CSS selector to enable working on them as 1.
9764  * @param {String/Array} selector The CSS selector or an array of elements
9765  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9766  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9767  * @return {CompositeElementLite/CompositeElement}
9768  * @member Roo
9769  * @method select
9770  */
9771 Roo.select = Roo.Element.select;
9772
9773
9774
9775
9776
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786 /*
9787  * Based on:
9788  * Ext JS Library 1.1.1
9789  * Copyright(c) 2006-2007, Ext JS, LLC.
9790  *
9791  * Originally Released Under LGPL - original licence link has changed is not relivant.
9792  *
9793  * Fork - LGPL
9794  * <script type="text/javascript">
9795  */
9796
9797
9798
9799 //Notifies Element that fx methods are available
9800 Roo.enableFx = true;
9801
9802 /**
9803  * @class Roo.Fx
9804  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9805  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9806  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9807  * Element effects to work.</p><br/>
9808  *
9809  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9810  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9811  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9812  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9813  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9814  * expected results and should be done with care.</p><br/>
9815  *
9816  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9817  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9818 <pre>
9819 Value  Description
9820 -----  -----------------------------
9821 tl     The top left corner
9822 t      The center of the top edge
9823 tr     The top right corner
9824 l      The center of the left edge
9825 r      The center of the right edge
9826 bl     The bottom left corner
9827 b      The center of the bottom edge
9828 br     The bottom right corner
9829 </pre>
9830  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9831  * below are common options that can be passed to any Fx method.</b>
9832  * @cfg {Function} callback A function called when the effect is finished
9833  * @cfg {Object} scope The scope of the effect function
9834  * @cfg {String} easing A valid Easing value for the effect
9835  * @cfg {String} afterCls A css class to apply after the effect
9836  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9837  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9838  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9839  * effects that end with the element being visually hidden, ignored otherwise)
9840  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9841  * a function which returns such a specification that will be applied to the Element after the effect finishes
9842  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9843  * @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
9844  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9845  */
9846 Roo.Fx = {
9847         /**
9848          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9849          * origin for the slide effect.  This function automatically handles wrapping the element with
9850          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9851          * Usage:
9852          *<pre><code>
9853 // default: slide the element in from the top
9854 el.slideIn();
9855
9856 // custom: slide the element in from the right with a 2-second duration
9857 el.slideIn('r', { duration: 2 });
9858
9859 // common config options shown with default values
9860 el.slideIn('t', {
9861     easing: 'easeOut',
9862     duration: .5
9863 });
9864 </code></pre>
9865          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9866          * @param {Object} options (optional) Object literal with any of the Fx config options
9867          * @return {Roo.Element} The Element
9868          */
9869     slideIn : function(anchor, o){
9870         var el = this.getFxEl();
9871         o = o || {};
9872
9873         el.queueFx(o, function(){
9874
9875             anchor = anchor || "t";
9876
9877             // fix display to visibility
9878             this.fixDisplay();
9879
9880             // restore values after effect
9881             var r = this.getFxRestore();
9882             var b = this.getBox();
9883             // fixed size for slide
9884             this.setSize(b);
9885
9886             // wrap if needed
9887             var wrap = this.fxWrap(r.pos, o, "hidden");
9888
9889             var st = this.dom.style;
9890             st.visibility = "visible";
9891             st.position = "absolute";
9892
9893             // clear out temp styles after slide and unwrap
9894             var after = function(){
9895                 el.fxUnwrap(wrap, r.pos, o);
9896                 st.width = r.width;
9897                 st.height = r.height;
9898                 el.afterFx(o);
9899             };
9900             // time to calc the positions
9901             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9902
9903             switch(anchor.toLowerCase()){
9904                 case "t":
9905                     wrap.setSize(b.width, 0);
9906                     st.left = st.bottom = "0";
9907                     a = {height: bh};
9908                 break;
9909                 case "l":
9910                     wrap.setSize(0, b.height);
9911                     st.right = st.top = "0";
9912                     a = {width: bw};
9913                 break;
9914                 case "r":
9915                     wrap.setSize(0, b.height);
9916                     wrap.setX(b.right);
9917                     st.left = st.top = "0";
9918                     a = {width: bw, points: pt};
9919                 break;
9920                 case "b":
9921                     wrap.setSize(b.width, 0);
9922                     wrap.setY(b.bottom);
9923                     st.left = st.top = "0";
9924                     a = {height: bh, points: pt};
9925                 break;
9926                 case "tl":
9927                     wrap.setSize(0, 0);
9928                     st.right = st.bottom = "0";
9929                     a = {width: bw, height: bh};
9930                 break;
9931                 case "bl":
9932                     wrap.setSize(0, 0);
9933                     wrap.setY(b.y+b.height);
9934                     st.right = st.top = "0";
9935                     a = {width: bw, height: bh, points: pt};
9936                 break;
9937                 case "br":
9938                     wrap.setSize(0, 0);
9939                     wrap.setXY([b.right, b.bottom]);
9940                     st.left = st.top = "0";
9941                     a = {width: bw, height: bh, points: pt};
9942                 break;
9943                 case "tr":
9944                     wrap.setSize(0, 0);
9945                     wrap.setX(b.x+b.width);
9946                     st.left = st.bottom = "0";
9947                     a = {width: bw, height: bh, points: pt};
9948                 break;
9949             }
9950             this.dom.style.visibility = "visible";
9951             wrap.show();
9952
9953             arguments.callee.anim = wrap.fxanim(a,
9954                 o,
9955                 'motion',
9956                 .5,
9957                 'easeOut', after);
9958         });
9959         return this;
9960     },
9961     
9962         /**
9963          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9964          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9965          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9966          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9967          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9968          * Usage:
9969          *<pre><code>
9970 // default: slide the element out to the top
9971 el.slideOut();
9972
9973 // custom: slide the element out to the right with a 2-second duration
9974 el.slideOut('r', { duration: 2 });
9975
9976 // common config options shown with default values
9977 el.slideOut('t', {
9978     easing: 'easeOut',
9979     duration: .5,
9980     remove: false,
9981     useDisplay: false
9982 });
9983 </code></pre>
9984          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9985          * @param {Object} options (optional) Object literal with any of the Fx config options
9986          * @return {Roo.Element} The Element
9987          */
9988     slideOut : function(anchor, o){
9989         var el = this.getFxEl();
9990         o = o || {};
9991
9992         el.queueFx(o, function(){
9993
9994             anchor = anchor || "t";
9995
9996             // restore values after effect
9997             var r = this.getFxRestore();
9998             
9999             var b = this.getBox();
10000             // fixed size for slide
10001             this.setSize(b);
10002
10003             // wrap if needed
10004             var wrap = this.fxWrap(r.pos, o, "visible");
10005
10006             var st = this.dom.style;
10007             st.visibility = "visible";
10008             st.position = "absolute";
10009
10010             wrap.setSize(b);
10011
10012             var after = function(){
10013                 if(o.useDisplay){
10014                     el.setDisplayed(false);
10015                 }else{
10016                     el.hide();
10017                 }
10018
10019                 el.fxUnwrap(wrap, r.pos, o);
10020
10021                 st.width = r.width;
10022                 st.height = r.height;
10023
10024                 el.afterFx(o);
10025             };
10026
10027             var a, zero = {to: 0};
10028             switch(anchor.toLowerCase()){
10029                 case "t":
10030                     st.left = st.bottom = "0";
10031                     a = {height: zero};
10032                 break;
10033                 case "l":
10034                     st.right = st.top = "0";
10035                     a = {width: zero};
10036                 break;
10037                 case "r":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, points: {to:[b.right, b.y]}};
10040                 break;
10041                 case "b":
10042                     st.left = st.top = "0";
10043                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10044                 break;
10045                 case "tl":
10046                     st.right = st.bottom = "0";
10047                     a = {width: zero, height: zero};
10048                 break;
10049                 case "bl":
10050                     st.right = st.top = "0";
10051                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10052                 break;
10053                 case "br":
10054                     st.left = st.top = "0";
10055                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10056                 break;
10057                 case "tr":
10058                     st.left = st.bottom = "0";
10059                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10060                 break;
10061             }
10062
10063             arguments.callee.anim = wrap.fxanim(a,
10064                 o,
10065                 'motion',
10066                 .5,
10067                 "easeOut", after);
10068         });
10069         return this;
10070     },
10071
10072         /**
10073          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10074          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10075          * The element must be removed from the DOM using the 'remove' config option if desired.
10076          * Usage:
10077          *<pre><code>
10078 // default
10079 el.puff();
10080
10081 // common config options shown with default values
10082 el.puff({
10083     easing: 'easeOut',
10084     duration: .5,
10085     remove: false,
10086     useDisplay: false
10087 });
10088 </code></pre>
10089          * @param {Object} options (optional) Object literal with any of the Fx config options
10090          * @return {Roo.Element} The Element
10091          */
10092     puff : function(o){
10093         var el = this.getFxEl();
10094         o = o || {};
10095
10096         el.queueFx(o, function(){
10097             this.clearOpacity();
10098             this.show();
10099
10100             // restore values after effect
10101             var r = this.getFxRestore();
10102             var st = this.dom.style;
10103
10104             var after = function(){
10105                 if(o.useDisplay){
10106                     el.setDisplayed(false);
10107                 }else{
10108                     el.hide();
10109                 }
10110
10111                 el.clearOpacity();
10112
10113                 el.setPositioning(r.pos);
10114                 st.width = r.width;
10115                 st.height = r.height;
10116                 st.fontSize = '';
10117                 el.afterFx(o);
10118             };
10119
10120             var width = this.getWidth();
10121             var height = this.getHeight();
10122
10123             arguments.callee.anim = this.fxanim({
10124                     width : {to: this.adjustWidth(width * 2)},
10125                     height : {to: this.adjustHeight(height * 2)},
10126                     points : {by: [-(width * .5), -(height * .5)]},
10127                     opacity : {to: 0},
10128                     fontSize: {to:200, unit: "%"}
10129                 },
10130                 o,
10131                 'motion',
10132                 .5,
10133                 "easeOut", after);
10134         });
10135         return this;
10136     },
10137
10138         /**
10139          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10140          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10141          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10142          * Usage:
10143          *<pre><code>
10144 // default
10145 el.switchOff();
10146
10147 // all config options shown with default values
10148 el.switchOff({
10149     easing: 'easeIn',
10150     duration: .3,
10151     remove: false,
10152     useDisplay: false
10153 });
10154 </code></pre>
10155          * @param {Object} options (optional) Object literal with any of the Fx config options
10156          * @return {Roo.Element} The Element
10157          */
10158     switchOff : function(o){
10159         var el = this.getFxEl();
10160         o = o || {};
10161
10162         el.queueFx(o, function(){
10163             this.clearOpacity();
10164             this.clip();
10165
10166             // restore values after effect
10167             var r = this.getFxRestore();
10168             var st = this.dom.style;
10169
10170             var after = function(){
10171                 if(o.useDisplay){
10172                     el.setDisplayed(false);
10173                 }else{
10174                     el.hide();
10175                 }
10176
10177                 el.clearOpacity();
10178                 el.setPositioning(r.pos);
10179                 st.width = r.width;
10180                 st.height = r.height;
10181
10182                 el.afterFx(o);
10183             };
10184
10185             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10186                 this.clearOpacity();
10187                 (function(){
10188                     this.fxanim({
10189                         height:{to:1},
10190                         points:{by:[0, this.getHeight() * .5]}
10191                     }, o, 'motion', 0.3, 'easeIn', after);
10192                 }).defer(100, this);
10193             });
10194         });
10195         return this;
10196     },
10197
10198     /**
10199      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10200      * changed using the "attr" config option) and then fading back to the original color. If no original
10201      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10202      * Usage:
10203 <pre><code>
10204 // default: highlight background to yellow
10205 el.highlight();
10206
10207 // custom: highlight foreground text to blue for 2 seconds
10208 el.highlight("0000ff", { attr: 'color', duration: 2 });
10209
10210 // common config options shown with default values
10211 el.highlight("ffff9c", {
10212     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10213     endColor: (current color) or "ffffff",
10214     easing: 'easeIn',
10215     duration: 1
10216 });
10217 </code></pre>
10218      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10219      * @param {Object} options (optional) Object literal with any of the Fx config options
10220      * @return {Roo.Element} The Element
10221      */ 
10222     highlight : function(color, o){
10223         var el = this.getFxEl();
10224         o = o || {};
10225
10226         el.queueFx(o, function(){
10227             color = color || "ffff9c";
10228             attr = o.attr || "backgroundColor";
10229
10230             this.clearOpacity();
10231             this.show();
10232
10233             var origColor = this.getColor(attr);
10234             var restoreColor = this.dom.style[attr];
10235             endColor = (o.endColor || origColor) || "ffffff";
10236
10237             var after = function(){
10238                 el.dom.style[attr] = restoreColor;
10239                 el.afterFx(o);
10240             };
10241
10242             var a = {};
10243             a[attr] = {from: color, to: endColor};
10244             arguments.callee.anim = this.fxanim(a,
10245                 o,
10246                 'color',
10247                 1,
10248                 'easeIn', after);
10249         });
10250         return this;
10251     },
10252
10253    /**
10254     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10255     * Usage:
10256 <pre><code>
10257 // default: a single light blue ripple
10258 el.frame();
10259
10260 // custom: 3 red ripples lasting 3 seconds total
10261 el.frame("ff0000", 3, { duration: 3 });
10262
10263 // common config options shown with default values
10264 el.frame("C3DAF9", 1, {
10265     duration: 1 //duration of entire animation (not each individual ripple)
10266     // Note: Easing is not configurable and will be ignored if included
10267 });
10268 </code></pre>
10269     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10270     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10271     * @param {Object} options (optional) Object literal with any of the Fx config options
10272     * @return {Roo.Element} The Element
10273     */
10274     frame : function(color, count, o){
10275         var el = this.getFxEl();
10276         o = o || {};
10277
10278         el.queueFx(o, function(){
10279             color = color || "#C3DAF9";
10280             if(color.length == 6){
10281                 color = "#" + color;
10282             }
10283             count = count || 1;
10284             duration = o.duration || 1;
10285             this.show();
10286
10287             var b = this.getBox();
10288             var animFn = function(){
10289                 var proxy = this.createProxy({
10290
10291                      style:{
10292                         visbility:"hidden",
10293                         position:"absolute",
10294                         "z-index":"35000", // yee haw
10295                         border:"0px solid " + color
10296                      }
10297                   });
10298                 var scale = Roo.isBorderBox ? 2 : 1;
10299                 proxy.animate({
10300                     top:{from:b.y, to:b.y - 20},
10301                     left:{from:b.x, to:b.x - 20},
10302                     borderWidth:{from:0, to:10},
10303                     opacity:{from:1, to:0},
10304                     height:{from:b.height, to:(b.height + (20*scale))},
10305                     width:{from:b.width, to:(b.width + (20*scale))}
10306                 }, duration, function(){
10307                     proxy.remove();
10308                 });
10309                 if(--count > 0){
10310                      animFn.defer((duration/2)*1000, this);
10311                 }else{
10312                     el.afterFx(o);
10313                 }
10314             };
10315             animFn.call(this);
10316         });
10317         return this;
10318     },
10319
10320    /**
10321     * Creates a pause before any subsequent queued effects begin.  If there are
10322     * no effects queued after the pause it will have no effect.
10323     * Usage:
10324 <pre><code>
10325 el.pause(1);
10326 </code></pre>
10327     * @param {Number} seconds The length of time to pause (in seconds)
10328     * @return {Roo.Element} The Element
10329     */
10330     pause : function(seconds){
10331         var el = this.getFxEl();
10332         var o = {};
10333
10334         el.queueFx(o, function(){
10335             setTimeout(function(){
10336                 el.afterFx(o);
10337             }, seconds * 1000);
10338         });
10339         return this;
10340     },
10341
10342    /**
10343     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10344     * using the "endOpacity" config option.
10345     * Usage:
10346 <pre><code>
10347 // default: fade in from opacity 0 to 100%
10348 el.fadeIn();
10349
10350 // custom: fade in from opacity 0 to 75% over 2 seconds
10351 el.fadeIn({ endOpacity: .75, duration: 2});
10352
10353 // common config options shown with default values
10354 el.fadeIn({
10355     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10356     easing: 'easeOut',
10357     duration: .5
10358 });
10359 </code></pre>
10360     * @param {Object} options (optional) Object literal with any of the Fx config options
10361     * @return {Roo.Element} The Element
10362     */
10363     fadeIn : function(o){
10364         var el = this.getFxEl();
10365         o = o || {};
10366         el.queueFx(o, function(){
10367             this.setOpacity(0);
10368             this.fixDisplay();
10369             this.dom.style.visibility = 'visible';
10370             var to = o.endOpacity || 1;
10371             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10372                 o, null, .5, "easeOut", function(){
10373                 if(to == 1){
10374                     this.clearOpacity();
10375                 }
10376                 el.afterFx(o);
10377             });
10378         });
10379         return this;
10380     },
10381
10382    /**
10383     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10384     * using the "endOpacity" config option.
10385     * Usage:
10386 <pre><code>
10387 // default: fade out from the element's current opacity to 0
10388 el.fadeOut();
10389
10390 // custom: fade out from the element's current opacity to 25% over 2 seconds
10391 el.fadeOut({ endOpacity: .25, duration: 2});
10392
10393 // common config options shown with default values
10394 el.fadeOut({
10395     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10396     easing: 'easeOut',
10397     duration: .5
10398     remove: false,
10399     useDisplay: false
10400 });
10401 </code></pre>
10402     * @param {Object} options (optional) Object literal with any of the Fx config options
10403     * @return {Roo.Element} The Element
10404     */
10405     fadeOut : function(o){
10406         var el = this.getFxEl();
10407         o = o || {};
10408         el.queueFx(o, function(){
10409             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10410                 o, null, .5, "easeOut", function(){
10411                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10412                      this.dom.style.display = "none";
10413                 }else{
10414                      this.dom.style.visibility = "hidden";
10415                 }
10416                 this.clearOpacity();
10417                 el.afterFx(o);
10418             });
10419         });
10420         return this;
10421     },
10422
10423    /**
10424     * Animates the transition of an element's dimensions from a starting height/width
10425     * to an ending height/width.
10426     * Usage:
10427 <pre><code>
10428 // change height and width to 100x100 pixels
10429 el.scale(100, 100);
10430
10431 // common config options shown with default values.  The height and width will default to
10432 // the element's existing values if passed as null.
10433 el.scale(
10434     [element's width],
10435     [element's height], {
10436     easing: 'easeOut',
10437     duration: .35
10438 });
10439 </code></pre>
10440     * @param {Number} width  The new width (pass undefined to keep the original width)
10441     * @param {Number} height  The new height (pass undefined to keep the original height)
10442     * @param {Object} options (optional) Object literal with any of the Fx config options
10443     * @return {Roo.Element} The Element
10444     */
10445     scale : function(w, h, o){
10446         this.shift(Roo.apply({}, o, {
10447             width: w,
10448             height: h
10449         }));
10450         return this;
10451     },
10452
10453    /**
10454     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10455     * Any of these properties not specified in the config object will not be changed.  This effect 
10456     * requires that at least one new dimension, position or opacity setting must be passed in on
10457     * the config object in order for the function to have any effect.
10458     * Usage:
10459 <pre><code>
10460 // slide the element horizontally to x position 200 while changing the height and opacity
10461 el.shift({ x: 200, height: 50, opacity: .8 });
10462
10463 // common config options shown with default values.
10464 el.shift({
10465     width: [element's width],
10466     height: [element's height],
10467     x: [element's x position],
10468     y: [element's y position],
10469     opacity: [element's opacity],
10470     easing: 'easeOut',
10471     duration: .35
10472 });
10473 </code></pre>
10474     * @param {Object} options  Object literal with any of the Fx config options
10475     * @return {Roo.Element} The Element
10476     */
10477     shift : function(o){
10478         var el = this.getFxEl();
10479         o = o || {};
10480         el.queueFx(o, function(){
10481             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10482             if(w !== undefined){
10483                 a.width = {to: this.adjustWidth(w)};
10484             }
10485             if(h !== undefined){
10486                 a.height = {to: this.adjustHeight(h)};
10487             }
10488             if(x !== undefined || y !== undefined){
10489                 a.points = {to: [
10490                     x !== undefined ? x : this.getX(),
10491                     y !== undefined ? y : this.getY()
10492                 ]};
10493             }
10494             if(op !== undefined){
10495                 a.opacity = {to: op};
10496             }
10497             if(o.xy !== undefined){
10498                 a.points = {to: o.xy};
10499             }
10500             arguments.callee.anim = this.fxanim(a,
10501                 o, 'motion', .35, "easeOut", function(){
10502                 el.afterFx(o);
10503             });
10504         });
10505         return this;
10506     },
10507
10508         /**
10509          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10510          * ending point of the effect.
10511          * Usage:
10512          *<pre><code>
10513 // default: slide the element downward while fading out
10514 el.ghost();
10515
10516 // custom: slide the element out to the right with a 2-second duration
10517 el.ghost('r', { duration: 2 });
10518
10519 // common config options shown with default values
10520 el.ghost('b', {
10521     easing: 'easeOut',
10522     duration: .5
10523     remove: false,
10524     useDisplay: false
10525 });
10526 </code></pre>
10527          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10528          * @param {Object} options (optional) Object literal with any of the Fx config options
10529          * @return {Roo.Element} The Element
10530          */
10531     ghost : function(anchor, o){
10532         var el = this.getFxEl();
10533         o = o || {};
10534
10535         el.queueFx(o, function(){
10536             anchor = anchor || "b";
10537
10538             // restore values after effect
10539             var r = this.getFxRestore();
10540             var w = this.getWidth(),
10541                 h = this.getHeight();
10542
10543             var st = this.dom.style;
10544
10545             var after = function(){
10546                 if(o.useDisplay){
10547                     el.setDisplayed(false);
10548                 }else{
10549                     el.hide();
10550                 }
10551
10552                 el.clearOpacity();
10553                 el.setPositioning(r.pos);
10554                 st.width = r.width;
10555                 st.height = r.height;
10556
10557                 el.afterFx(o);
10558             };
10559
10560             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10561             switch(anchor.toLowerCase()){
10562                 case "t":
10563                     pt.by = [0, -h];
10564                 break;
10565                 case "l":
10566                     pt.by = [-w, 0];
10567                 break;
10568                 case "r":
10569                     pt.by = [w, 0];
10570                 break;
10571                 case "b":
10572                     pt.by = [0, h];
10573                 break;
10574                 case "tl":
10575                     pt.by = [-w, -h];
10576                 break;
10577                 case "bl":
10578                     pt.by = [-w, h];
10579                 break;
10580                 case "br":
10581                     pt.by = [w, h];
10582                 break;
10583                 case "tr":
10584                     pt.by = [w, -h];
10585                 break;
10586             }
10587
10588             arguments.callee.anim = this.fxanim(a,
10589                 o,
10590                 'motion',
10591                 .5,
10592                 "easeOut", after);
10593         });
10594         return this;
10595     },
10596
10597         /**
10598          * Ensures that all effects queued after syncFx is called on the element are
10599          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10600          * @return {Roo.Element} The Element
10601          */
10602     syncFx : function(){
10603         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10604             block : false,
10605             concurrent : true,
10606             stopFx : false
10607         });
10608         return this;
10609     },
10610
10611         /**
10612          * Ensures that all effects queued after sequenceFx is called on the element are
10613          * run in sequence.  This is the opposite of {@link #syncFx}.
10614          * @return {Roo.Element} The Element
10615          */
10616     sequenceFx : function(){
10617         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10618             block : false,
10619             concurrent : false,
10620             stopFx : false
10621         });
10622         return this;
10623     },
10624
10625         /* @private */
10626     nextFx : function(){
10627         var ef = this.fxQueue[0];
10628         if(ef){
10629             ef.call(this);
10630         }
10631     },
10632
10633         /**
10634          * Returns true if the element has any effects actively running or queued, else returns false.
10635          * @return {Boolean} True if element has active effects, else false
10636          */
10637     hasActiveFx : function(){
10638         return this.fxQueue && this.fxQueue[0];
10639     },
10640
10641         /**
10642          * Stops any running effects and clears the element's internal effects queue if it contains
10643          * any additional effects that haven't started yet.
10644          * @return {Roo.Element} The Element
10645          */
10646     stopFx : function(){
10647         if(this.hasActiveFx()){
10648             var cur = this.fxQueue[0];
10649             if(cur && cur.anim && cur.anim.isAnimated()){
10650                 this.fxQueue = [cur]; // clear out others
10651                 cur.anim.stop(true);
10652             }
10653         }
10654         return this;
10655     },
10656
10657         /* @private */
10658     beforeFx : function(o){
10659         if(this.hasActiveFx() && !o.concurrent){
10660            if(o.stopFx){
10661                this.stopFx();
10662                return true;
10663            }
10664            return false;
10665         }
10666         return true;
10667     },
10668
10669         /**
10670          * Returns true if the element is currently blocking so that no other effect can be queued
10671          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10672          * used to ensure that an effect initiated by a user action runs to completion prior to the
10673          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10674          * @return {Boolean} True if blocking, else false
10675          */
10676     hasFxBlock : function(){
10677         var q = this.fxQueue;
10678         return q && q[0] && q[0].block;
10679     },
10680
10681         /* @private */
10682     queueFx : function(o, fn){
10683         if(!this.fxQueue){
10684             this.fxQueue = [];
10685         }
10686         if(!this.hasFxBlock()){
10687             Roo.applyIf(o, this.fxDefaults);
10688             if(!o.concurrent){
10689                 var run = this.beforeFx(o);
10690                 fn.block = o.block;
10691                 this.fxQueue.push(fn);
10692                 if(run){
10693                     this.nextFx();
10694                 }
10695             }else{
10696                 fn.call(this);
10697             }
10698         }
10699         return this;
10700     },
10701
10702         /* @private */
10703     fxWrap : function(pos, o, vis){
10704         var wrap;
10705         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10706             var wrapXY;
10707             if(o.fixPosition){
10708                 wrapXY = this.getXY();
10709             }
10710             var div = document.createElement("div");
10711             div.style.visibility = vis;
10712             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10713             wrap.setPositioning(pos);
10714             if(wrap.getStyle("position") == "static"){
10715                 wrap.position("relative");
10716             }
10717             this.clearPositioning('auto');
10718             wrap.clip();
10719             wrap.dom.appendChild(this.dom);
10720             if(wrapXY){
10721                 wrap.setXY(wrapXY);
10722             }
10723         }
10724         return wrap;
10725     },
10726
10727         /* @private */
10728     fxUnwrap : function(wrap, pos, o){
10729         this.clearPositioning();
10730         this.setPositioning(pos);
10731         if(!o.wrap){
10732             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10733             wrap.remove();
10734         }
10735     },
10736
10737         /* @private */
10738     getFxRestore : function(){
10739         var st = this.dom.style;
10740         return {pos: this.getPositioning(), width: st.width, height : st.height};
10741     },
10742
10743         /* @private */
10744     afterFx : function(o){
10745         if(o.afterStyle){
10746             this.applyStyles(o.afterStyle);
10747         }
10748         if(o.afterCls){
10749             this.addClass(o.afterCls);
10750         }
10751         if(o.remove === true){
10752             this.remove();
10753         }
10754         Roo.callback(o.callback, o.scope, [this]);
10755         if(!o.concurrent){
10756             this.fxQueue.shift();
10757             this.nextFx();
10758         }
10759     },
10760
10761         /* @private */
10762     getFxEl : function(){ // support for composite element fx
10763         return Roo.get(this.dom);
10764     },
10765
10766         /* @private */
10767     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10768         animType = animType || 'run';
10769         opt = opt || {};
10770         var anim = Roo.lib.Anim[animType](
10771             this.dom, args,
10772             (opt.duration || defaultDur) || .35,
10773             (opt.easing || defaultEase) || 'easeOut',
10774             function(){
10775                 Roo.callback(cb, this);
10776             },
10777             this
10778         );
10779         opt.anim = anim;
10780         return anim;
10781     }
10782 };
10783
10784 // backwords compat
10785 Roo.Fx.resize = Roo.Fx.scale;
10786
10787 //When included, Roo.Fx is automatically applied to Element so that all basic
10788 //effects are available directly via the Element API
10789 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10790  * Based on:
10791  * Ext JS Library 1.1.1
10792  * Copyright(c) 2006-2007, Ext JS, LLC.
10793  *
10794  * Originally Released Under LGPL - original licence link has changed is not relivant.
10795  *
10796  * Fork - LGPL
10797  * <script type="text/javascript">
10798  */
10799
10800
10801 /**
10802  * @class Roo.CompositeElement
10803  * Standard composite class. Creates a Roo.Element for every element in the collection.
10804  * <br><br>
10805  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10806  * actions will be performed on all the elements in this collection.</b>
10807  * <br><br>
10808  * All methods return <i>this</i> and can be chained.
10809  <pre><code>
10810  var els = Roo.select("#some-el div.some-class", true);
10811  // or select directly from an existing element
10812  var el = Roo.get('some-el');
10813  el.select('div.some-class', true);
10814
10815  els.setWidth(100); // all elements become 100 width
10816  els.hide(true); // all elements fade out and hide
10817  // or
10818  els.setWidth(100).hide(true);
10819  </code></pre>
10820  */
10821 Roo.CompositeElement = function(els){
10822     this.elements = [];
10823     this.addElements(els);
10824 };
10825 Roo.CompositeElement.prototype = {
10826     isComposite: true,
10827     addElements : function(els){
10828         if(!els) return this;
10829         if(typeof els == "string"){
10830             els = Roo.Element.selectorFunction(els);
10831         }
10832         var yels = this.elements;
10833         var index = yels.length-1;
10834         for(var i = 0, len = els.length; i < len; i++) {
10835                 yels[++index] = Roo.get(els[i]);
10836         }
10837         return this;
10838     },
10839
10840     /**
10841     * Clears this composite and adds the elements returned by the passed selector.
10842     * @param {String/Array} els A string CSS selector, an array of elements or an element
10843     * @return {CompositeElement} this
10844     */
10845     fill : function(els){
10846         this.elements = [];
10847         this.add(els);
10848         return this;
10849     },
10850
10851     /**
10852     * Filters this composite to only elements that match the passed selector.
10853     * @param {String} selector A string CSS selector
10854     * @return {CompositeElement} this
10855     */
10856     filter : function(selector){
10857         var els = [];
10858         this.each(function(el){
10859             if(el.is(selector)){
10860                 els[els.length] = el.dom;
10861             }
10862         });
10863         this.fill(els);
10864         return this;
10865     },
10866
10867     invoke : function(fn, args){
10868         var els = this.elements;
10869         for(var i = 0, len = els.length; i < len; i++) {
10870                 Roo.Element.prototype[fn].apply(els[i], args);
10871         }
10872         return this;
10873     },
10874     /**
10875     * Adds elements to this composite.
10876     * @param {String/Array} els A string CSS selector, an array of elements or an element
10877     * @return {CompositeElement} this
10878     */
10879     add : function(els){
10880         if(typeof els == "string"){
10881             this.addElements(Roo.Element.selectorFunction(els));
10882         }else if(els.length !== undefined){
10883             this.addElements(els);
10884         }else{
10885             this.addElements([els]);
10886         }
10887         return this;
10888     },
10889     /**
10890     * Calls the passed function passing (el, this, index) for each element in this composite.
10891     * @param {Function} fn The function to call
10892     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10893     * @return {CompositeElement} this
10894     */
10895     each : function(fn, scope){
10896         var els = this.elements;
10897         for(var i = 0, len = els.length; i < len; i++){
10898             if(fn.call(scope || els[i], els[i], this, i) === false) {
10899                 break;
10900             }
10901         }
10902         return this;
10903     },
10904
10905     /**
10906      * Returns the Element object at the specified index
10907      * @param {Number} index
10908      * @return {Roo.Element}
10909      */
10910     item : function(index){
10911         return this.elements[index] || null;
10912     },
10913
10914     /**
10915      * Returns the first Element
10916      * @return {Roo.Element}
10917      */
10918     first : function(){
10919         return this.item(0);
10920     },
10921
10922     /**
10923      * Returns the last Element
10924      * @return {Roo.Element}
10925      */
10926     last : function(){
10927         return this.item(this.elements.length-1);
10928     },
10929
10930     /**
10931      * Returns the number of elements in this composite
10932      * @return Number
10933      */
10934     getCount : function(){
10935         return this.elements.length;
10936     },
10937
10938     /**
10939      * Returns true if this composite contains the passed element
10940      * @return Boolean
10941      */
10942     contains : function(el){
10943         return this.indexOf(el) !== -1;
10944     },
10945
10946     /**
10947      * Returns true if this composite contains the passed element
10948      * @return Boolean
10949      */
10950     indexOf : function(el){
10951         return this.elements.indexOf(Roo.get(el));
10952     },
10953
10954
10955     /**
10956     * Removes the specified element(s).
10957     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10958     * or an array of any of those.
10959     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10960     * @return {CompositeElement} this
10961     */
10962     removeElement : function(el, removeDom){
10963         if(el instanceof Array){
10964             for(var i = 0, len = el.length; i < len; i++){
10965                 this.removeElement(el[i]);
10966             }
10967             return this;
10968         }
10969         var index = typeof el == 'number' ? el : this.indexOf(el);
10970         if(index !== -1){
10971             if(removeDom){
10972                 var d = this.elements[index];
10973                 if(d.dom){
10974                     d.remove();
10975                 }else{
10976                     d.parentNode.removeChild(d);
10977                 }
10978             }
10979             this.elements.splice(index, 1);
10980         }
10981         return this;
10982     },
10983
10984     /**
10985     * Replaces the specified element with the passed element.
10986     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10987     * to replace.
10988     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10989     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10990     * @return {CompositeElement} this
10991     */
10992     replaceElement : function(el, replacement, domReplace){
10993         var index = typeof el == 'number' ? el : this.indexOf(el);
10994         if(index !== -1){
10995             if(domReplace){
10996                 this.elements[index].replaceWith(replacement);
10997             }else{
10998                 this.elements.splice(index, 1, Roo.get(replacement))
10999             }
11000         }
11001         return this;
11002     },
11003
11004     /**
11005      * Removes all elements.
11006      */
11007     clear : function(){
11008         this.elements = [];
11009     }
11010 };
11011 (function(){
11012     Roo.CompositeElement.createCall = function(proto, fnName){
11013         if(!proto[fnName]){
11014             proto[fnName] = function(){
11015                 return this.invoke(fnName, arguments);
11016             };
11017         }
11018     };
11019     for(var fnName in Roo.Element.prototype){
11020         if(typeof Roo.Element.prototype[fnName] == "function"){
11021             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11022         }
11023     };
11024 })();
11025 /*
11026  * Based on:
11027  * Ext JS Library 1.1.1
11028  * Copyright(c) 2006-2007, Ext JS, LLC.
11029  *
11030  * Originally Released Under LGPL - original licence link has changed is not relivant.
11031  *
11032  * Fork - LGPL
11033  * <script type="text/javascript">
11034  */
11035
11036 /**
11037  * @class Roo.CompositeElementLite
11038  * @extends Roo.CompositeElement
11039  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11040  <pre><code>
11041  var els = Roo.select("#some-el div.some-class");
11042  // or select directly from an existing element
11043  var el = Roo.get('some-el');
11044  el.select('div.some-class');
11045
11046  els.setWidth(100); // all elements become 100 width
11047  els.hide(true); // all elements fade out and hide
11048  // or
11049  els.setWidth(100).hide(true);
11050  </code></pre><br><br>
11051  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11052  * actions will be performed on all the elements in this collection.</b>
11053  */
11054 Roo.CompositeElementLite = function(els){
11055     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11056     this.el = new Roo.Element.Flyweight();
11057 };
11058 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11059     addElements : function(els){
11060         if(els){
11061             if(els instanceof Array){
11062                 this.elements = this.elements.concat(els);
11063             }else{
11064                 var yels = this.elements;
11065                 var index = yels.length-1;
11066                 for(var i = 0, len = els.length; i < len; i++) {
11067                     yels[++index] = els[i];
11068                 }
11069             }
11070         }
11071         return this;
11072     },
11073     invoke : function(fn, args){
11074         var els = this.elements;
11075         var el = this.el;
11076         for(var i = 0, len = els.length; i < len; i++) {
11077             el.dom = els[i];
11078                 Roo.Element.prototype[fn].apply(el, args);
11079         }
11080         return this;
11081     },
11082     /**
11083      * Returns a flyweight Element of the dom element object at the specified index
11084      * @param {Number} index
11085      * @return {Roo.Element}
11086      */
11087     item : function(index){
11088         if(!this.elements[index]){
11089             return null;
11090         }
11091         this.el.dom = this.elements[index];
11092         return this.el;
11093     },
11094
11095     // fixes scope with flyweight
11096     addListener : function(eventName, handler, scope, opt){
11097         var els = this.elements;
11098         for(var i = 0, len = els.length; i < len; i++) {
11099             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11100         }
11101         return this;
11102     },
11103
11104     /**
11105     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11106     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11107     * a reference to the dom node, use el.dom.</b>
11108     * @param {Function} fn The function to call
11109     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11110     * @return {CompositeElement} this
11111     */
11112     each : function(fn, scope){
11113         var els = this.elements;
11114         var el = this.el;
11115         for(var i = 0, len = els.length; i < len; i++){
11116             el.dom = els[i];
11117                 if(fn.call(scope || el, el, this, i) === false){
11118                 break;
11119             }
11120         }
11121         return this;
11122     },
11123
11124     indexOf : function(el){
11125         return this.elements.indexOf(Roo.getDom(el));
11126     },
11127
11128     replaceElement : function(el, replacement, domReplace){
11129         var index = typeof el == 'number' ? el : this.indexOf(el);
11130         if(index !== -1){
11131             replacement = Roo.getDom(replacement);
11132             if(domReplace){
11133                 var d = this.elements[index];
11134                 d.parentNode.insertBefore(replacement, d);
11135                 d.parentNode.removeChild(d);
11136             }
11137             this.elements.splice(index, 1, replacement);
11138         }
11139         return this;
11140     }
11141 });
11142 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11143
11144 /*
11145  * Based on:
11146  * Ext JS Library 1.1.1
11147  * Copyright(c) 2006-2007, Ext JS, LLC.
11148  *
11149  * Originally Released Under LGPL - original licence link has changed is not relivant.
11150  *
11151  * Fork - LGPL
11152  * <script type="text/javascript">
11153  */
11154
11155  
11156
11157 /**
11158  * @class Roo.data.Connection
11159  * @extends Roo.util.Observable
11160  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11161  * either to a configured URL, or to a URL specified at request time.<br><br>
11162  * <p>
11163  * Requests made by this class are asynchronous, and will return immediately. No data from
11164  * the server will be available to the statement immediately following the {@link #request} call.
11165  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11166  * <p>
11167  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11168  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11169  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11170  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11171  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11172  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11173  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11174  * standard DOM methods.
11175  * @constructor
11176  * @param {Object} config a configuration object.
11177  */
11178 Roo.data.Connection = function(config){
11179     Roo.apply(this, config);
11180     this.addEvents({
11181         /**
11182          * @event beforerequest
11183          * Fires before a network request is made to retrieve a data object.
11184          * @param {Connection} conn This Connection object.
11185          * @param {Object} options The options config object passed to the {@link #request} method.
11186          */
11187         "beforerequest" : true,
11188         /**
11189          * @event requestcomplete
11190          * Fires if the request was successfully completed.
11191          * @param {Connection} conn This Connection object.
11192          * @param {Object} response The XHR object containing the response data.
11193          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11194          * @param {Object} options The options config object passed to the {@link #request} method.
11195          */
11196         "requestcomplete" : true,
11197         /**
11198          * @event requestexception
11199          * Fires if an error HTTP status was returned from the server.
11200          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11201          * @param {Connection} conn This Connection object.
11202          * @param {Object} response The XHR object containing the response data.
11203          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11204          * @param {Object} options The options config object passed to the {@link #request} method.
11205          */
11206         "requestexception" : true
11207     });
11208     Roo.data.Connection.superclass.constructor.call(this);
11209 };
11210
11211 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11212     /**
11213      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11214      */
11215     /**
11216      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11217      * extra parameters to each request made by this object. (defaults to undefined)
11218      */
11219     /**
11220      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11221      *  to each request made by this object. (defaults to undefined)
11222      */
11223     /**
11224      * @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)
11225      */
11226     /**
11227      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11228      */
11229     timeout : 30000,
11230     /**
11231      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11232      * @type Boolean
11233      */
11234     autoAbort:false,
11235
11236     /**
11237      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11238      * @type Boolean
11239      */
11240     disableCaching: true,
11241
11242     /**
11243      * Sends an HTTP request to a remote server.
11244      * @param {Object} options An object which may contain the following properties:<ul>
11245      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11246      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11247      * request, a url encoded string or a function to call to get either.</li>
11248      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11249      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11250      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11251      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11252      * <li>options {Object} The parameter to the request call.</li>
11253      * <li>success {Boolean} True if the request succeeded.</li>
11254      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11255      * </ul></li>
11256      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11257      * The callback is passed the following parameters:<ul>
11258      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11259      * <li>options {Object} The parameter to the request call.</li>
11260      * </ul></li>
11261      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11262      * The callback is passed the following parameters:<ul>
11263      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11264      * <li>options {Object} The parameter to the request call.</li>
11265      * </ul></li>
11266      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11267      * for the callback function. Defaults to the browser window.</li>
11268      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11269      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11270      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11271      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11272      * params for the post data. Any params will be appended to the URL.</li>
11273      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11274      * </ul>
11275      * @return {Number} transactionId
11276      */
11277     request : function(o){
11278         if(this.fireEvent("beforerequest", this, o) !== false){
11279             var p = o.params;
11280
11281             if(typeof p == "function"){
11282                 p = p.call(o.scope||window, o);
11283             }
11284             if(typeof p == "object"){
11285                 p = Roo.urlEncode(o.params);
11286             }
11287             if(this.extraParams){
11288                 var extras = Roo.urlEncode(this.extraParams);
11289                 p = p ? (p + '&' + extras) : extras;
11290             }
11291
11292             var url = o.url || this.url;
11293             if(typeof url == 'function'){
11294                 url = url.call(o.scope||window, o);
11295             }
11296
11297             if(o.form){
11298                 var form = Roo.getDom(o.form);
11299                 url = url || form.action;
11300
11301                 var enctype = form.getAttribute("enctype");
11302                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11303                     return this.doFormUpload(o, p, url);
11304                 }
11305                 var f = Roo.lib.Ajax.serializeForm(form);
11306                 p = p ? (p + '&' + f) : f;
11307             }
11308
11309             var hs = o.headers;
11310             if(this.defaultHeaders){
11311                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11312                 if(!o.headers){
11313                     o.headers = hs;
11314                 }
11315             }
11316
11317             var cb = {
11318                 success: this.handleResponse,
11319                 failure: this.handleFailure,
11320                 scope: this,
11321                 argument: {options: o},
11322                 timeout : this.timeout
11323             };
11324
11325             var method = o.method||this.method||(p ? "POST" : "GET");
11326
11327             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11328                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11329             }
11330
11331             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11332                 if(o.autoAbort){
11333                     this.abort();
11334                 }
11335             }else if(this.autoAbort !== false){
11336                 this.abort();
11337             }
11338
11339             if((method == 'GET' && p) || o.xmlData){
11340                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11341                 p = '';
11342             }
11343             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11344             return this.transId;
11345         }else{
11346             Roo.callback(o.callback, o.scope, [o, null, null]);
11347             return null;
11348         }
11349     },
11350
11351     /**
11352      * Determine whether this object has a request outstanding.
11353      * @param {Number} transactionId (Optional) defaults to the last transaction
11354      * @return {Boolean} True if there is an outstanding request.
11355      */
11356     isLoading : function(transId){
11357         if(transId){
11358             return Roo.lib.Ajax.isCallInProgress(transId);
11359         }else{
11360             return this.transId ? true : false;
11361         }
11362     },
11363
11364     /**
11365      * Aborts any outstanding request.
11366      * @param {Number} transactionId (Optional) defaults to the last transaction
11367      */
11368     abort : function(transId){
11369         if(transId || this.isLoading()){
11370             Roo.lib.Ajax.abort(transId || this.transId);
11371         }
11372     },
11373
11374     // private
11375     handleResponse : function(response){
11376         this.transId = false;
11377         var options = response.argument.options;
11378         response.argument = options ? options.argument : null;
11379         this.fireEvent("requestcomplete", this, response, options);
11380         Roo.callback(options.success, options.scope, [response, options]);
11381         Roo.callback(options.callback, options.scope, [options, true, response]);
11382     },
11383
11384     // private
11385     handleFailure : function(response, e){
11386         this.transId = false;
11387         var options = response.argument.options;
11388         response.argument = options ? options.argument : null;
11389         this.fireEvent("requestexception", this, response, options, e);
11390         Roo.callback(options.failure, options.scope, [response, options]);
11391         Roo.callback(options.callback, options.scope, [options, false, response]);
11392     },
11393
11394     // private
11395     doFormUpload : function(o, ps, url){
11396         var id = Roo.id();
11397         var frame = document.createElement('iframe');
11398         frame.id = id;
11399         frame.name = id;
11400         frame.className = 'x-hidden';
11401         if(Roo.isIE){
11402             frame.src = Roo.SSL_SECURE_URL;
11403         }
11404         document.body.appendChild(frame);
11405
11406         if(Roo.isIE){
11407            document.frames[id].name = id;
11408         }
11409
11410         var form = Roo.getDom(o.form);
11411         form.target = id;
11412         form.method = 'POST';
11413         form.enctype = form.encoding = 'multipart/form-data';
11414         if(url){
11415             form.action = url;
11416         }
11417
11418         var hiddens, hd;
11419         if(ps){ // add dynamic params
11420             hiddens = [];
11421             ps = Roo.urlDecode(ps, false);
11422             for(var k in ps){
11423                 if(ps.hasOwnProperty(k)){
11424                     hd = document.createElement('input');
11425                     hd.type = 'hidden';
11426                     hd.name = k;
11427                     hd.value = ps[k];
11428                     form.appendChild(hd);
11429                     hiddens.push(hd);
11430                 }
11431             }
11432         }
11433
11434         function cb(){
11435             var r = {  // bogus response object
11436                 responseText : '',
11437                 responseXML : null
11438             };
11439
11440             r.argument = o ? o.argument : null;
11441
11442             try { //
11443                 var doc;
11444                 if(Roo.isIE){
11445                     doc = frame.contentWindow.document;
11446                 }else {
11447                     doc = (frame.contentDocument || window.frames[id].document);
11448                 }
11449                 if(doc && doc.body){
11450                     r.responseText = doc.body.innerHTML;
11451                 }
11452                 if(doc && doc.XMLDocument){
11453                     r.responseXML = doc.XMLDocument;
11454                 }else {
11455                     r.responseXML = doc;
11456                 }
11457             }
11458             catch(e) {
11459                 // ignore
11460             }
11461
11462             Roo.EventManager.removeListener(frame, 'load', cb, this);
11463
11464             this.fireEvent("requestcomplete", this, r, o);
11465             Roo.callback(o.success, o.scope, [r, o]);
11466             Roo.callback(o.callback, o.scope, [o, true, r]);
11467
11468             setTimeout(function(){document.body.removeChild(frame);}, 100);
11469         }
11470
11471         Roo.EventManager.on(frame, 'load', cb, this);
11472         form.submit();
11473
11474         if(hiddens){ // remove dynamic params
11475             for(var i = 0, len = hiddens.length; i < len; i++){
11476                 form.removeChild(hiddens[i]);
11477             }
11478         }
11479     }
11480 });
11481
11482 /**
11483  * @class Roo.Ajax
11484  * @extends Roo.data.Connection
11485  * Global Ajax request class.
11486  *
11487  * @singleton
11488  */
11489 Roo.Ajax = new Roo.data.Connection({
11490     // fix up the docs
11491    /**
11492      * @cfg {String} url @hide
11493      */
11494     /**
11495      * @cfg {Object} extraParams @hide
11496      */
11497     /**
11498      * @cfg {Object} defaultHeaders @hide
11499      */
11500     /**
11501      * @cfg {String} method (Optional) @hide
11502      */
11503     /**
11504      * @cfg {Number} timeout (Optional) @hide
11505      */
11506     /**
11507      * @cfg {Boolean} autoAbort (Optional) @hide
11508      */
11509
11510     /**
11511      * @cfg {Boolean} disableCaching (Optional) @hide
11512      */
11513
11514     /**
11515      * @property  disableCaching
11516      * True to add a unique cache-buster param to GET requests. (defaults to true)
11517      * @type Boolean
11518      */
11519     /**
11520      * @property  url
11521      * The default URL to be used for requests to the server. (defaults to undefined)
11522      * @type String
11523      */
11524     /**
11525      * @property  extraParams
11526      * An object containing properties which are used as
11527      * extra parameters to each request made by this object. (defaults to undefined)
11528      * @type Object
11529      */
11530     /**
11531      * @property  defaultHeaders
11532      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11533      * @type Object
11534      */
11535     /**
11536      * @property  method
11537      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11538      * @type String
11539      */
11540     /**
11541      * @property  timeout
11542      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11543      * @type Number
11544      */
11545
11546     /**
11547      * @property  autoAbort
11548      * Whether a new request should abort any pending requests. (defaults to false)
11549      * @type Boolean
11550      */
11551     autoAbort : false,
11552
11553     /**
11554      * Serialize the passed form into a url encoded string
11555      * @param {String/HTMLElement} form
11556      * @return {String}
11557      */
11558     serializeForm : function(form){
11559         return Roo.lib.Ajax.serializeForm(form);
11560     }
11561 });/*
11562  * Based on:
11563  * Ext JS Library 1.1.1
11564  * Copyright(c) 2006-2007, Ext JS, LLC.
11565  *
11566  * Originally Released Under LGPL - original licence link has changed is not relivant.
11567  *
11568  * Fork - LGPL
11569  * <script type="text/javascript">
11570  */
11571  
11572 /**
11573  * Global Ajax request class.
11574  * 
11575  * @class Roo.Ajax
11576  * @extends Roo.data.Connection
11577  * @static
11578  * 
11579  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11580  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11581  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11582  * @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)
11583  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11584  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11585  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11586  */
11587 Roo.Ajax = new Roo.data.Connection({
11588     // fix up the docs
11589     /**
11590      * @scope Roo.Ajax
11591      * @type {Boolear} 
11592      */
11593     autoAbort : false,
11594
11595     /**
11596      * Serialize the passed form into a url encoded string
11597      * @scope Roo.Ajax
11598      * @param {String/HTMLElement} form
11599      * @return {String}
11600      */
11601     serializeForm : function(form){
11602         return Roo.lib.Ajax.serializeForm(form);
11603     }
11604 });/*
11605  * Based on:
11606  * Ext JS Library 1.1.1
11607  * Copyright(c) 2006-2007, Ext JS, LLC.
11608  *
11609  * Originally Released Under LGPL - original licence link has changed is not relivant.
11610  *
11611  * Fork - LGPL
11612  * <script type="text/javascript">
11613  */
11614
11615  
11616 /**
11617  * @class Roo.UpdateManager
11618  * @extends Roo.util.Observable
11619  * Provides AJAX-style update for Element object.<br><br>
11620  * Usage:<br>
11621  * <pre><code>
11622  * // Get it from a Roo.Element object
11623  * var el = Roo.get("foo");
11624  * var mgr = el.getUpdateManager();
11625  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11626  * ...
11627  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11628  * <br>
11629  * // or directly (returns the same UpdateManager instance)
11630  * var mgr = new Roo.UpdateManager("myElementId");
11631  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11632  * mgr.on("update", myFcnNeedsToKnow);
11633  * <br>
11634    // short handed call directly from the element object
11635    Roo.get("foo").load({
11636         url: "bar.php",
11637         scripts:true,
11638         params: "for=bar",
11639         text: "Loading Foo..."
11640    });
11641  * </code></pre>
11642  * @constructor
11643  * Create new UpdateManager directly.
11644  * @param {String/HTMLElement/Roo.Element} el The element to update
11645  * @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).
11646  */
11647 Roo.UpdateManager = function(el, forceNew){
11648     el = Roo.get(el);
11649     if(!forceNew && el.updateManager){
11650         return el.updateManager;
11651     }
11652     /**
11653      * The Element object
11654      * @type Roo.Element
11655      */
11656     this.el = el;
11657     /**
11658      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11659      * @type String
11660      */
11661     this.defaultUrl = null;
11662
11663     this.addEvents({
11664         /**
11665          * @event beforeupdate
11666          * Fired before an update is made, return false from your handler and the update is cancelled.
11667          * @param {Roo.Element} el
11668          * @param {String/Object/Function} url
11669          * @param {String/Object} params
11670          */
11671         "beforeupdate": true,
11672         /**
11673          * @event update
11674          * Fired after successful update is made.
11675          * @param {Roo.Element} el
11676          * @param {Object} oResponseObject The response Object
11677          */
11678         "update": true,
11679         /**
11680          * @event failure
11681          * Fired on update failure.
11682          * @param {Roo.Element} el
11683          * @param {Object} oResponseObject The response Object
11684          */
11685         "failure": true
11686     });
11687     var d = Roo.UpdateManager.defaults;
11688     /**
11689      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11690      * @type String
11691      */
11692     this.sslBlankUrl = d.sslBlankUrl;
11693     /**
11694      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11695      * @type Boolean
11696      */
11697     this.disableCaching = d.disableCaching;
11698     /**
11699      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11700      * @type String
11701      */
11702     this.indicatorText = d.indicatorText;
11703     /**
11704      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11705      * @type String
11706      */
11707     this.showLoadIndicator = d.showLoadIndicator;
11708     /**
11709      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11710      * @type Number
11711      */
11712     this.timeout = d.timeout;
11713
11714     /**
11715      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11716      * @type Boolean
11717      */
11718     this.loadScripts = d.loadScripts;
11719
11720     /**
11721      * Transaction object of current executing transaction
11722      */
11723     this.transaction = null;
11724
11725     /**
11726      * @private
11727      */
11728     this.autoRefreshProcId = null;
11729     /**
11730      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11731      * @type Function
11732      */
11733     this.refreshDelegate = this.refresh.createDelegate(this);
11734     /**
11735      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11736      * @type Function
11737      */
11738     this.updateDelegate = this.update.createDelegate(this);
11739     /**
11740      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11741      * @type Function
11742      */
11743     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11744     /**
11745      * @private
11746      */
11747     this.successDelegate = this.processSuccess.createDelegate(this);
11748     /**
11749      * @private
11750      */
11751     this.failureDelegate = this.processFailure.createDelegate(this);
11752
11753     if(!this.renderer){
11754      /**
11755       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11756       */
11757     this.renderer = new Roo.UpdateManager.BasicRenderer();
11758     }
11759     
11760     Roo.UpdateManager.superclass.constructor.call(this);
11761 };
11762
11763 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11764     /**
11765      * Get the Element this UpdateManager is bound to
11766      * @return {Roo.Element} The element
11767      */
11768     getEl : function(){
11769         return this.el;
11770     },
11771     /**
11772      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11773      * @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:
11774 <pre><code>
11775 um.update({<br/>
11776     url: "your-url.php",<br/>
11777     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11778     callback: yourFunction,<br/>
11779     scope: yourObject, //(optional scope)  <br/>
11780     discardUrl: false, <br/>
11781     nocache: false,<br/>
11782     text: "Loading...",<br/>
11783     timeout: 30,<br/>
11784     scripts: false<br/>
11785 });
11786 </code></pre>
11787      * The only required property is url. The optional properties nocache, text and scripts
11788      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11789      * @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}
11790      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11791      * @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.
11792      */
11793     update : function(url, params, callback, discardUrl){
11794         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11795             var method = this.method, cfg;
11796             if(typeof url == "object"){ // must be config object
11797                 cfg = url;
11798                 url = cfg.url;
11799                 params = params || cfg.params;
11800                 callback = callback || cfg.callback;
11801                 discardUrl = discardUrl || cfg.discardUrl;
11802                 if(callback && cfg.scope){
11803                     callback = callback.createDelegate(cfg.scope);
11804                 }
11805                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11806                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11807                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11808                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11809                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11810             }
11811             this.showLoading();
11812             if(!discardUrl){
11813                 this.defaultUrl = url;
11814             }
11815             if(typeof url == "function"){
11816                 url = url.call(this);
11817             }
11818
11819             method = method || (params ? "POST" : "GET");
11820             if(method == "GET"){
11821                 url = this.prepareUrl(url);
11822             }
11823
11824             var o = Roo.apply(cfg ||{}, {
11825                 url : url,
11826                 params: params,
11827                 success: this.successDelegate,
11828                 failure: this.failureDelegate,
11829                 callback: undefined,
11830                 timeout: (this.timeout*1000),
11831                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11832             });
11833
11834             this.transaction = Roo.Ajax.request(o);
11835         }
11836     },
11837
11838     /**
11839      * 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.
11840      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11841      * @param {String/HTMLElement} form The form Id or form element
11842      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11843      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11844      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11845      */
11846     formUpdate : function(form, url, reset, callback){
11847         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11848             if(typeof url == "function"){
11849                 url = url.call(this);
11850             }
11851             form = Roo.getDom(form);
11852             this.transaction = Roo.Ajax.request({
11853                 form: form,
11854                 url:url,
11855                 success: this.successDelegate,
11856                 failure: this.failureDelegate,
11857                 timeout: (this.timeout*1000),
11858                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11859             });
11860             this.showLoading.defer(1, this);
11861         }
11862     },
11863
11864     /**
11865      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11866      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11867      */
11868     refresh : function(callback){
11869         if(this.defaultUrl == null){
11870             return;
11871         }
11872         this.update(this.defaultUrl, null, callback, true);
11873     },
11874
11875     /**
11876      * Set this element to auto refresh.
11877      * @param {Number} interval How often to update (in seconds).
11878      * @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)
11879      * @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}
11880      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11881      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11882      */
11883     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11884         if(refreshNow){
11885             this.update(url || this.defaultUrl, params, callback, true);
11886         }
11887         if(this.autoRefreshProcId){
11888             clearInterval(this.autoRefreshProcId);
11889         }
11890         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11891     },
11892
11893     /**
11894      * Stop auto refresh on this element.
11895      */
11896      stopAutoRefresh : function(){
11897         if(this.autoRefreshProcId){
11898             clearInterval(this.autoRefreshProcId);
11899             delete this.autoRefreshProcId;
11900         }
11901     },
11902
11903     isAutoRefreshing : function(){
11904        return this.autoRefreshProcId ? true : false;
11905     },
11906     /**
11907      * Called to update the element to "Loading" state. Override to perform custom action.
11908      */
11909     showLoading : function(){
11910         if(this.showLoadIndicator){
11911             this.el.update(this.indicatorText);
11912         }
11913     },
11914
11915     /**
11916      * Adds unique parameter to query string if disableCaching = true
11917      * @private
11918      */
11919     prepareUrl : function(url){
11920         if(this.disableCaching){
11921             var append = "_dc=" + (new Date().getTime());
11922             if(url.indexOf("?") !== -1){
11923                 url += "&" + append;
11924             }else{
11925                 url += "?" + append;
11926             }
11927         }
11928         return url;
11929     },
11930
11931     /**
11932      * @private
11933      */
11934     processSuccess : function(response){
11935         this.transaction = null;
11936         if(response.argument.form && response.argument.reset){
11937             try{ // put in try/catch since some older FF releases had problems with this
11938                 response.argument.form.reset();
11939             }catch(e){}
11940         }
11941         if(this.loadScripts){
11942             this.renderer.render(this.el, response, this,
11943                 this.updateComplete.createDelegate(this, [response]));
11944         }else{
11945             this.renderer.render(this.el, response, this);
11946             this.updateComplete(response);
11947         }
11948     },
11949
11950     updateComplete : function(response){
11951         this.fireEvent("update", this.el, response);
11952         if(typeof response.argument.callback == "function"){
11953             response.argument.callback(this.el, true, response);
11954         }
11955     },
11956
11957     /**
11958      * @private
11959      */
11960     processFailure : function(response){
11961         this.transaction = null;
11962         this.fireEvent("failure", this.el, response);
11963         if(typeof response.argument.callback == "function"){
11964             response.argument.callback(this.el, false, response);
11965         }
11966     },
11967
11968     /**
11969      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11970      * @param {Object} renderer The object implementing the render() method
11971      */
11972     setRenderer : function(renderer){
11973         this.renderer = renderer;
11974     },
11975
11976     getRenderer : function(){
11977        return this.renderer;
11978     },
11979
11980     /**
11981      * Set the defaultUrl used for updates
11982      * @param {String/Function} defaultUrl The url or a function to call to get the url
11983      */
11984     setDefaultUrl : function(defaultUrl){
11985         this.defaultUrl = defaultUrl;
11986     },
11987
11988     /**
11989      * Aborts the executing transaction
11990      */
11991     abort : function(){
11992         if(this.transaction){
11993             Roo.Ajax.abort(this.transaction);
11994         }
11995     },
11996
11997     /**
11998      * Returns true if an update is in progress
11999      * @return {Boolean}
12000      */
12001     isUpdating : function(){
12002         if(this.transaction){
12003             return Roo.Ajax.isLoading(this.transaction);
12004         }
12005         return false;
12006     }
12007 });
12008
12009 /**
12010  * @class Roo.UpdateManager.defaults
12011  * @static (not really - but it helps the doc tool)
12012  * The defaults collection enables customizing the default properties of UpdateManager
12013  */
12014    Roo.UpdateManager.defaults = {
12015        /**
12016          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12017          * @type Number
12018          */
12019          timeout : 30,
12020
12021          /**
12022          * True to process scripts by default (Defaults to false).
12023          * @type Boolean
12024          */
12025         loadScripts : false,
12026
12027         /**
12028         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12029         * @type String
12030         */
12031         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12032         /**
12033          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12034          * @type Boolean
12035          */
12036         disableCaching : false,
12037         /**
12038          * Whether to show indicatorText when loading (Defaults to true).
12039          * @type Boolean
12040          */
12041         showLoadIndicator : true,
12042         /**
12043          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12044          * @type String
12045          */
12046         indicatorText : '<div class="loading-indicator">Loading...</div>'
12047    };
12048
12049 /**
12050  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12051  *Usage:
12052  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12053  * @param {String/HTMLElement/Roo.Element} el The element to update
12054  * @param {String} url The url
12055  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12056  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12057  * @static
12058  * @deprecated
12059  * @member Roo.UpdateManager
12060  */
12061 Roo.UpdateManager.updateElement = function(el, url, params, options){
12062     var um = Roo.get(el, true).getUpdateManager();
12063     Roo.apply(um, options);
12064     um.update(url, params, options ? options.callback : null);
12065 };
12066 // alias for backwards compat
12067 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12068 /**
12069  * @class Roo.UpdateManager.BasicRenderer
12070  * Default Content renderer. Updates the elements innerHTML with the responseText.
12071  */
12072 Roo.UpdateManager.BasicRenderer = function(){};
12073
12074 Roo.UpdateManager.BasicRenderer.prototype = {
12075     /**
12076      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12077      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12078      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12079      * @param {Roo.Element} el The element being rendered
12080      * @param {Object} response The YUI Connect response object
12081      * @param {UpdateManager} updateManager The calling update manager
12082      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12083      */
12084      render : function(el, response, updateManager, callback){
12085         el.update(response.responseText, updateManager.loadScripts, callback);
12086     }
12087 };
12088 /*
12089  * Based on:
12090  * Ext JS Library 1.1.1
12091  * Copyright(c) 2006-2007, Ext JS, LLC.
12092  *
12093  * Originally Released Under LGPL - original licence link has changed is not relivant.
12094  *
12095  * Fork - LGPL
12096  * <script type="text/javascript">
12097  */
12098
12099 /**
12100  * @class Roo.util.DelayedTask
12101  * Provides a convenient method of performing setTimeout where a new
12102  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12103  * You can use this class to buffer
12104  * the keypress events for a certain number of milliseconds, and perform only if they stop
12105  * for that amount of time.
12106  * @constructor The parameters to this constructor serve as defaults and are not required.
12107  * @param {Function} fn (optional) The default function to timeout
12108  * @param {Object} scope (optional) The default scope of that timeout
12109  * @param {Array} args (optional) The default Array of arguments
12110  */
12111 Roo.util.DelayedTask = function(fn, scope, args){
12112     var id = null, d, t;
12113
12114     var call = function(){
12115         var now = new Date().getTime();
12116         if(now - t >= d){
12117             clearInterval(id);
12118             id = null;
12119             fn.apply(scope, args || []);
12120         }
12121     };
12122     /**
12123      * Cancels any pending timeout and queues a new one
12124      * @param {Number} delay The milliseconds to delay
12125      * @param {Function} newFn (optional) Overrides function passed to constructor
12126      * @param {Object} newScope (optional) Overrides scope passed to constructor
12127      * @param {Array} newArgs (optional) Overrides args passed to constructor
12128      */
12129     this.delay = function(delay, newFn, newScope, newArgs){
12130         if(id && delay != d){
12131             this.cancel();
12132         }
12133         d = delay;
12134         t = new Date().getTime();
12135         fn = newFn || fn;
12136         scope = newScope || scope;
12137         args = newArgs || args;
12138         if(!id){
12139             id = setInterval(call, d);
12140         }
12141     };
12142
12143     /**
12144      * Cancel the last queued timeout
12145      */
12146     this.cancel = function(){
12147         if(id){
12148             clearInterval(id);
12149             id = null;
12150         }
12151     };
12152 };/*
12153  * Based on:
12154  * Ext JS Library 1.1.1
12155  * Copyright(c) 2006-2007, Ext JS, LLC.
12156  *
12157  * Originally Released Under LGPL - original licence link has changed is not relivant.
12158  *
12159  * Fork - LGPL
12160  * <script type="text/javascript">
12161  */
12162  
12163  
12164 Roo.util.TaskRunner = function(interval){
12165     interval = interval || 10;
12166     var tasks = [], removeQueue = [];
12167     var id = 0;
12168     var running = false;
12169
12170     var stopThread = function(){
12171         running = false;
12172         clearInterval(id);
12173         id = 0;
12174     };
12175
12176     var startThread = function(){
12177         if(!running){
12178             running = true;
12179             id = setInterval(runTasks, interval);
12180         }
12181     };
12182
12183     var removeTask = function(task){
12184         removeQueue.push(task);
12185         if(task.onStop){
12186             task.onStop();
12187         }
12188     };
12189
12190     var runTasks = function(){
12191         if(removeQueue.length > 0){
12192             for(var i = 0, len = removeQueue.length; i < len; i++){
12193                 tasks.remove(removeQueue[i]);
12194             }
12195             removeQueue = [];
12196             if(tasks.length < 1){
12197                 stopThread();
12198                 return;
12199             }
12200         }
12201         var now = new Date().getTime();
12202         for(var i = 0, len = tasks.length; i < len; ++i){
12203             var t = tasks[i];
12204             var itime = now - t.taskRunTime;
12205             if(t.interval <= itime){
12206                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12207                 t.taskRunTime = now;
12208                 if(rt === false || t.taskRunCount === t.repeat){
12209                     removeTask(t);
12210                     return;
12211                 }
12212             }
12213             if(t.duration && t.duration <= (now - t.taskStartTime)){
12214                 removeTask(t);
12215             }
12216         }
12217     };
12218
12219     /**
12220      * Queues a new task.
12221      * @param {Object} task
12222      */
12223     this.start = function(task){
12224         tasks.push(task);
12225         task.taskStartTime = new Date().getTime();
12226         task.taskRunTime = 0;
12227         task.taskRunCount = 0;
12228         startThread();
12229         return task;
12230     };
12231
12232     this.stop = function(task){
12233         removeTask(task);
12234         return task;
12235     };
12236
12237     this.stopAll = function(){
12238         stopThread();
12239         for(var i = 0, len = tasks.length; i < len; i++){
12240             if(tasks[i].onStop){
12241                 tasks[i].onStop();
12242             }
12243         }
12244         tasks = [];
12245         removeQueue = [];
12246     };
12247 };
12248
12249 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12250  * Based on:
12251  * Ext JS Library 1.1.1
12252  * Copyright(c) 2006-2007, Ext JS, LLC.
12253  *
12254  * Originally Released Under LGPL - original licence link has changed is not relivant.
12255  *
12256  * Fork - LGPL
12257  * <script type="text/javascript">
12258  */
12259
12260  
12261 /**
12262  * @class Roo.util.MixedCollection
12263  * @extends Roo.util.Observable
12264  * A Collection class that maintains both numeric indexes and keys and exposes events.
12265  * @constructor
12266  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12267  * collection (defaults to false)
12268  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12269  * and return the key value for that item.  This is used when available to look up the key on items that
12270  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12271  * equivalent to providing an implementation for the {@link #getKey} method.
12272  */
12273 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12274     this.items = [];
12275     this.map = {};
12276     this.keys = [];
12277     this.length = 0;
12278     this.addEvents({
12279         /**
12280          * @event clear
12281          * Fires when the collection is cleared.
12282          */
12283         "clear" : true,
12284         /**
12285          * @event add
12286          * Fires when an item is added to the collection.
12287          * @param {Number} index The index at which the item was added.
12288          * @param {Object} o The item added.
12289          * @param {String} key The key associated with the added item.
12290          */
12291         "add" : true,
12292         /**
12293          * @event replace
12294          * Fires when an item is replaced in the collection.
12295          * @param {String} key he key associated with the new added.
12296          * @param {Object} old The item being replaced.
12297          * @param {Object} new The new item.
12298          */
12299         "replace" : true,
12300         /**
12301          * @event remove
12302          * Fires when an item is removed from the collection.
12303          * @param {Object} o The item being removed.
12304          * @param {String} key (optional) The key associated with the removed item.
12305          */
12306         "remove" : true,
12307         "sort" : true
12308     });
12309     this.allowFunctions = allowFunctions === true;
12310     if(keyFn){
12311         this.getKey = keyFn;
12312     }
12313     Roo.util.MixedCollection.superclass.constructor.call(this);
12314 };
12315
12316 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12317     allowFunctions : false,
12318     
12319 /**
12320  * Adds an item to the collection.
12321  * @param {String} key The key to associate with the item
12322  * @param {Object} o The item to add.
12323  * @return {Object} The item added.
12324  */
12325     add : function(key, o){
12326         if(arguments.length == 1){
12327             o = arguments[0];
12328             key = this.getKey(o);
12329         }
12330         if(typeof key == "undefined" || key === null){
12331             this.length++;
12332             this.items.push(o);
12333             this.keys.push(null);
12334         }else{
12335             var old = this.map[key];
12336             if(old){
12337                 return this.replace(key, o);
12338             }
12339             this.length++;
12340             this.items.push(o);
12341             this.map[key] = o;
12342             this.keys.push(key);
12343         }
12344         this.fireEvent("add", this.length-1, o, key);
12345         return o;
12346     },
12347        
12348 /**
12349   * MixedCollection has a generic way to fetch keys if you implement getKey.
12350 <pre><code>
12351 // normal way
12352 var mc = new Roo.util.MixedCollection();
12353 mc.add(someEl.dom.id, someEl);
12354 mc.add(otherEl.dom.id, otherEl);
12355 //and so on
12356
12357 // using getKey
12358 var mc = new Roo.util.MixedCollection();
12359 mc.getKey = function(el){
12360    return el.dom.id;
12361 };
12362 mc.add(someEl);
12363 mc.add(otherEl);
12364
12365 // or via the constructor
12366 var mc = new Roo.util.MixedCollection(false, function(el){
12367    return el.dom.id;
12368 });
12369 mc.add(someEl);
12370 mc.add(otherEl);
12371 </code></pre>
12372  * @param o {Object} The item for which to find the key.
12373  * @return {Object} The key for the passed item.
12374  */
12375     getKey : function(o){
12376          return o.id; 
12377     },
12378    
12379 /**
12380  * Replaces an item in the collection.
12381  * @param {String} key The key associated with the item to replace, or the item to replace.
12382  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12383  * @return {Object}  The new item.
12384  */
12385     replace : function(key, o){
12386         if(arguments.length == 1){
12387             o = arguments[0];
12388             key = this.getKey(o);
12389         }
12390         var old = this.item(key);
12391         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12392              return this.add(key, o);
12393         }
12394         var index = this.indexOfKey(key);
12395         this.items[index] = o;
12396         this.map[key] = o;
12397         this.fireEvent("replace", key, old, o);
12398         return o;
12399     },
12400    
12401 /**
12402  * Adds all elements of an Array or an Object to the collection.
12403  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12404  * an Array of values, each of which are added to the collection.
12405  */
12406     addAll : function(objs){
12407         if(arguments.length > 1 || objs instanceof Array){
12408             var args = arguments.length > 1 ? arguments : objs;
12409             for(var i = 0, len = args.length; i < len; i++){
12410                 this.add(args[i]);
12411             }
12412         }else{
12413             for(var key in objs){
12414                 if(this.allowFunctions || typeof objs[key] != "function"){
12415                     this.add(key, objs[key]);
12416                 }
12417             }
12418         }
12419     },
12420    
12421 /**
12422  * Executes the specified function once for every item in the collection, passing each
12423  * item as the first and only parameter. returning false from the function will stop the iteration.
12424  * @param {Function} fn The function to execute for each item.
12425  * @param {Object} scope (optional) The scope in which to execute the function.
12426  */
12427     each : function(fn, scope){
12428         var items = [].concat(this.items); // each safe for removal
12429         for(var i = 0, len = items.length; i < len; i++){
12430             if(fn.call(scope || items[i], items[i], i, len) === false){
12431                 break;
12432             }
12433         }
12434     },
12435    
12436 /**
12437  * Executes the specified function once for every key in the collection, passing each
12438  * key, and its associated item as the first two parameters.
12439  * @param {Function} fn The function to execute for each item.
12440  * @param {Object} scope (optional) The scope in which to execute the function.
12441  */
12442     eachKey : function(fn, scope){
12443         for(var i = 0, len = this.keys.length; i < len; i++){
12444             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12445         }
12446     },
12447    
12448 /**
12449  * Returns the first item in the collection which elicits a true return value from the
12450  * passed selection function.
12451  * @param {Function} fn The selection function to execute for each item.
12452  * @param {Object} scope (optional) The scope in which to execute the function.
12453  * @return {Object} The first item in the collection which returned true from the selection function.
12454  */
12455     find : function(fn, scope){
12456         for(var i = 0, len = this.items.length; i < len; i++){
12457             if(fn.call(scope || window, this.items[i], this.keys[i])){
12458                 return this.items[i];
12459             }
12460         }
12461         return null;
12462     },
12463    
12464 /**
12465  * Inserts an item at the specified index in the collection.
12466  * @param {Number} index The index to insert the item at.
12467  * @param {String} key The key to associate with the new item, or the item itself.
12468  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12469  * @return {Object} The item inserted.
12470  */
12471     insert : function(index, key, o){
12472         if(arguments.length == 2){
12473             o = arguments[1];
12474             key = this.getKey(o);
12475         }
12476         if(index >= this.length){
12477             return this.add(key, o);
12478         }
12479         this.length++;
12480         this.items.splice(index, 0, o);
12481         if(typeof key != "undefined" && key != null){
12482             this.map[key] = o;
12483         }
12484         this.keys.splice(index, 0, key);
12485         this.fireEvent("add", index, o, key);
12486         return o;
12487     },
12488    
12489 /**
12490  * Removed an item from the collection.
12491  * @param {Object} o The item to remove.
12492  * @return {Object} The item removed.
12493  */
12494     remove : function(o){
12495         return this.removeAt(this.indexOf(o));
12496     },
12497    
12498 /**
12499  * Remove an item from a specified index in the collection.
12500  * @param {Number} index The index within the collection of the item to remove.
12501  */
12502     removeAt : function(index){
12503         if(index < this.length && index >= 0){
12504             this.length--;
12505             var o = this.items[index];
12506             this.items.splice(index, 1);
12507             var key = this.keys[index];
12508             if(typeof key != "undefined"){
12509                 delete this.map[key];
12510             }
12511             this.keys.splice(index, 1);
12512             this.fireEvent("remove", o, key);
12513         }
12514     },
12515    
12516 /**
12517  * Removed an item associated with the passed key fom the collection.
12518  * @param {String} key The key of the item to remove.
12519  */
12520     removeKey : function(key){
12521         return this.removeAt(this.indexOfKey(key));
12522     },
12523    
12524 /**
12525  * Returns the number of items in the collection.
12526  * @return {Number} the number of items in the collection.
12527  */
12528     getCount : function(){
12529         return this.length; 
12530     },
12531    
12532 /**
12533  * Returns index within the collection of the passed Object.
12534  * @param {Object} o The item to find the index of.
12535  * @return {Number} index of the item.
12536  */
12537     indexOf : function(o){
12538         if(!this.items.indexOf){
12539             for(var i = 0, len = this.items.length; i < len; i++){
12540                 if(this.items[i] == o) return i;
12541             }
12542             return -1;
12543         }else{
12544             return this.items.indexOf(o);
12545         }
12546     },
12547    
12548 /**
12549  * Returns index within the collection of the passed key.
12550  * @param {String} key The key to find the index of.
12551  * @return {Number} index of the key.
12552  */
12553     indexOfKey : function(key){
12554         if(!this.keys.indexOf){
12555             for(var i = 0, len = this.keys.length; i < len; i++){
12556                 if(this.keys[i] == key) return i;
12557             }
12558             return -1;
12559         }else{
12560             return this.keys.indexOf(key);
12561         }
12562     },
12563    
12564 /**
12565  * Returns the item associated with the passed key OR index. Key has priority over index.
12566  * @param {String/Number} key The key or index of the item.
12567  * @return {Object} The item associated with the passed key.
12568  */
12569     item : function(key){
12570         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12571         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12572     },
12573     
12574 /**
12575  * Returns the item at the specified index.
12576  * @param {Number} index The index of the item.
12577  * @return {Object}
12578  */
12579     itemAt : function(index){
12580         return this.items[index];
12581     },
12582     
12583 /**
12584  * Returns the item associated with the passed key.
12585  * @param {String/Number} key The key of the item.
12586  * @return {Object} The item associated with the passed key.
12587  */
12588     key : function(key){
12589         return this.map[key];
12590     },
12591    
12592 /**
12593  * Returns true if the collection contains the passed Object as an item.
12594  * @param {Object} o  The Object to look for in the collection.
12595  * @return {Boolean} True if the collection contains the Object as an item.
12596  */
12597     contains : function(o){
12598         return this.indexOf(o) != -1;
12599     },
12600    
12601 /**
12602  * Returns true if the collection contains the passed Object as a key.
12603  * @param {String} key The key to look for in the collection.
12604  * @return {Boolean} True if the collection contains the Object as a key.
12605  */
12606     containsKey : function(key){
12607         return typeof this.map[key] != "undefined";
12608     },
12609    
12610 /**
12611  * Removes all items from the collection.
12612  */
12613     clear : function(){
12614         this.length = 0;
12615         this.items = [];
12616         this.keys = [];
12617         this.map = {};
12618         this.fireEvent("clear");
12619     },
12620    
12621 /**
12622  * Returns the first item in the collection.
12623  * @return {Object} the first item in the collection..
12624  */
12625     first : function(){
12626         return this.items[0]; 
12627     },
12628    
12629 /**
12630  * Returns the last item in the collection.
12631  * @return {Object} the last item in the collection..
12632  */
12633     last : function(){
12634         return this.items[this.length-1];   
12635     },
12636     
12637     _sort : function(property, dir, fn){
12638         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12639         fn = fn || function(a, b){
12640             return a-b;
12641         };
12642         var c = [], k = this.keys, items = this.items;
12643         for(var i = 0, len = items.length; i < len; i++){
12644             c[c.length] = {key: k[i], value: items[i], index: i};
12645         }
12646         c.sort(function(a, b){
12647             var v = fn(a[property], b[property]) * dsc;
12648             if(v == 0){
12649                 v = (a.index < b.index ? -1 : 1);
12650             }
12651             return v;
12652         });
12653         for(var i = 0, len = c.length; i < len; i++){
12654             items[i] = c[i].value;
12655             k[i] = c[i].key;
12656         }
12657         this.fireEvent("sort", this);
12658     },
12659     
12660     /**
12661      * Sorts this collection with the passed comparison function
12662      * @param {String} direction (optional) "ASC" or "DESC"
12663      * @param {Function} fn (optional) comparison function
12664      */
12665     sort : function(dir, fn){
12666         this._sort("value", dir, fn);
12667     },
12668     
12669     /**
12670      * Sorts this collection by keys
12671      * @param {String} direction (optional) "ASC" or "DESC"
12672      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12673      */
12674     keySort : function(dir, fn){
12675         this._sort("key", dir, fn || function(a, b){
12676             return String(a).toUpperCase()-String(b).toUpperCase();
12677         });
12678     },
12679     
12680     /**
12681      * Returns a range of items in this collection
12682      * @param {Number} startIndex (optional) defaults to 0
12683      * @param {Number} endIndex (optional) default to the last item
12684      * @return {Array} An array of items
12685      */
12686     getRange : function(start, end){
12687         var items = this.items;
12688         if(items.length < 1){
12689             return [];
12690         }
12691         start = start || 0;
12692         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12693         var r = [];
12694         if(start <= end){
12695             for(var i = start; i <= end; i++) {
12696                     r[r.length] = items[i];
12697             }
12698         }else{
12699             for(var i = start; i >= end; i--) {
12700                     r[r.length] = items[i];
12701             }
12702         }
12703         return r;
12704     },
12705         
12706     /**
12707      * Filter the <i>objects</i> in this collection by a specific property. 
12708      * Returns a new collection that has been filtered.
12709      * @param {String} property A property on your objects
12710      * @param {String/RegExp} value Either string that the property values 
12711      * should start with or a RegExp to test against the property
12712      * @return {MixedCollection} The new filtered collection
12713      */
12714     filter : function(property, value){
12715         if(!value.exec){ // not a regex
12716             value = String(value);
12717             if(value.length == 0){
12718                 return this.clone();
12719             }
12720             value = new RegExp("^" + Roo.escapeRe(value), "i");
12721         }
12722         return this.filterBy(function(o){
12723             return o && value.test(o[property]);
12724         });
12725         },
12726     
12727     /**
12728      * Filter by a function. * Returns a new collection that has been filtered.
12729      * The passed function will be called with each 
12730      * object in the collection. If the function returns true, the value is included 
12731      * otherwise it is filtered.
12732      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12733      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12734      * @return {MixedCollection} The new filtered collection
12735      */
12736     filterBy : function(fn, scope){
12737         var r = new Roo.util.MixedCollection();
12738         r.getKey = this.getKey;
12739         var k = this.keys, it = this.items;
12740         for(var i = 0, len = it.length; i < len; i++){
12741             if(fn.call(scope||this, it[i], k[i])){
12742                                 r.add(k[i], it[i]);
12743                         }
12744         }
12745         return r;
12746     },
12747     
12748     /**
12749      * Creates a duplicate of this collection
12750      * @return {MixedCollection}
12751      */
12752     clone : function(){
12753         var r = new Roo.util.MixedCollection();
12754         var k = this.keys, it = this.items;
12755         for(var i = 0, len = it.length; i < len; i++){
12756             r.add(k[i], it[i]);
12757         }
12758         r.getKey = this.getKey;
12759         return r;
12760     }
12761 });
12762 /**
12763  * Returns the item associated with the passed key or index.
12764  * @method
12765  * @param {String/Number} key The key or index of the item.
12766  * @return {Object} The item associated with the passed key.
12767  */
12768 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12769  * Based on:
12770  * Ext JS Library 1.1.1
12771  * Copyright(c) 2006-2007, Ext JS, LLC.
12772  *
12773  * Originally Released Under LGPL - original licence link has changed is not relivant.
12774  *
12775  * Fork - LGPL
12776  * <script type="text/javascript">
12777  */
12778 /**
12779  * @class Roo.util.JSON
12780  * Modified version of Douglas Crockford"s json.js that doesn"t
12781  * mess with the Object prototype 
12782  * http://www.json.org/js.html
12783  * @singleton
12784  */
12785 Roo.util.JSON = new (function(){
12786     var useHasOwn = {}.hasOwnProperty ? true : false;
12787     
12788     // crashes Safari in some instances
12789     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12790     
12791     var pad = function(n) {
12792         return n < 10 ? "0" + n : n;
12793     };
12794     
12795     var m = {
12796         "\b": '\\b',
12797         "\t": '\\t',
12798         "\n": '\\n',
12799         "\f": '\\f',
12800         "\r": '\\r',
12801         '"' : '\\"',
12802         "\\": '\\\\'
12803     };
12804
12805     var encodeString = function(s){
12806         if (/["\\\x00-\x1f]/.test(s)) {
12807             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12808                 var c = m[b];
12809                 if(c){
12810                     return c;
12811                 }
12812                 c = b.charCodeAt();
12813                 return "\\u00" +
12814                     Math.floor(c / 16).toString(16) +
12815                     (c % 16).toString(16);
12816             }) + '"';
12817         }
12818         return '"' + s + '"';
12819     };
12820     
12821     var encodeArray = function(o){
12822         var a = ["["], b, i, l = o.length, v;
12823             for (i = 0; i < l; i += 1) {
12824                 v = o[i];
12825                 switch (typeof v) {
12826                     case "undefined":
12827                     case "function":
12828                     case "unknown":
12829                         break;
12830                     default:
12831                         if (b) {
12832                             a.push(',');
12833                         }
12834                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12835                         b = true;
12836                 }
12837             }
12838             a.push("]");
12839             return a.join("");
12840     };
12841     
12842     var encodeDate = function(o){
12843         return '"' + o.getFullYear() + "-" +
12844                 pad(o.getMonth() + 1) + "-" +
12845                 pad(o.getDate()) + "T" +
12846                 pad(o.getHours()) + ":" +
12847                 pad(o.getMinutes()) + ":" +
12848                 pad(o.getSeconds()) + '"';
12849     };
12850     
12851     /**
12852      * Encodes an Object, Array or other value
12853      * @param {Mixed} o The variable to encode
12854      * @return {String} The JSON string
12855      */
12856     this.encode = function(o)
12857     {
12858         // should this be extended to fully wrap stringify..
12859         
12860         if(typeof o == "undefined" || o === null){
12861             return "null";
12862         }else if(o instanceof Array){
12863             return encodeArray(o);
12864         }else if(o instanceof Date){
12865             return encodeDate(o);
12866         }else if(typeof o == "string"){
12867             return encodeString(o);
12868         }else if(typeof o == "number"){
12869             return isFinite(o) ? String(o) : "null";
12870         }else if(typeof o == "boolean"){
12871             return String(o);
12872         }else {
12873             var a = ["{"], b, i, v;
12874             for (i in o) {
12875                 if(!useHasOwn || o.hasOwnProperty(i)) {
12876                     v = o[i];
12877                     switch (typeof v) {
12878                     case "undefined":
12879                     case "function":
12880                     case "unknown":
12881                         break;
12882                     default:
12883                         if(b){
12884                             a.push(',');
12885                         }
12886                         a.push(this.encode(i), ":",
12887                                 v === null ? "null" : this.encode(v));
12888                         b = true;
12889                     }
12890                 }
12891             }
12892             a.push("}");
12893             return a.join("");
12894         }
12895     };
12896     
12897     /**
12898      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12899      * @param {String} json The JSON string
12900      * @return {Object} The resulting object
12901      */
12902     this.decode = function(json){
12903         
12904         return  /** eval:var:json */ eval("(" + json + ')');
12905     };
12906 })();
12907 /** 
12908  * Shorthand for {@link Roo.util.JSON#encode}
12909  * @member Roo encode 
12910  * @method */
12911 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12912 /** 
12913  * Shorthand for {@link Roo.util.JSON#decode}
12914  * @member Roo decode 
12915  * @method */
12916 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12917 /*
12918  * Based on:
12919  * Ext JS Library 1.1.1
12920  * Copyright(c) 2006-2007, Ext JS, LLC.
12921  *
12922  * Originally Released Under LGPL - original licence link has changed is not relivant.
12923  *
12924  * Fork - LGPL
12925  * <script type="text/javascript">
12926  */
12927  
12928 /**
12929  * @class Roo.util.Format
12930  * Reusable data formatting functions
12931  * @singleton
12932  */
12933 Roo.util.Format = function(){
12934     var trimRe = /^\s+|\s+$/g;
12935     return {
12936         /**
12937          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12938          * @param {String} value The string to truncate
12939          * @param {Number} length The maximum length to allow before truncating
12940          * @return {String} The converted text
12941          */
12942         ellipsis : function(value, len){
12943             if(value && value.length > len){
12944                 return value.substr(0, len-3)+"...";
12945             }
12946             return value;
12947         },
12948
12949         /**
12950          * Checks a reference and converts it to empty string if it is undefined
12951          * @param {Mixed} value Reference to check
12952          * @return {Mixed} Empty string if converted, otherwise the original value
12953          */
12954         undef : function(value){
12955             return typeof value != "undefined" ? value : "";
12956         },
12957
12958         /**
12959          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12960          * @param {String} value The string to encode
12961          * @return {String} The encoded text
12962          */
12963         htmlEncode : function(value){
12964             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12965         },
12966
12967         /**
12968          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12969          * @param {String} value The string to decode
12970          * @return {String} The decoded text
12971          */
12972         htmlDecode : function(value){
12973             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12974         },
12975
12976         /**
12977          * Trims any whitespace from either side of a string
12978          * @param {String} value The text to trim
12979          * @return {String} The trimmed text
12980          */
12981         trim : function(value){
12982             return String(value).replace(trimRe, "");
12983         },
12984
12985         /**
12986          * Returns a substring from within an original string
12987          * @param {String} value The original text
12988          * @param {Number} start The start index of the substring
12989          * @param {Number} length The length of the substring
12990          * @return {String} The substring
12991          */
12992         substr : function(value, start, length){
12993             return String(value).substr(start, length);
12994         },
12995
12996         /**
12997          * Converts a string to all lower case letters
12998          * @param {String} value The text to convert
12999          * @return {String} The converted text
13000          */
13001         lowercase : function(value){
13002             return String(value).toLowerCase();
13003         },
13004
13005         /**
13006          * Converts a string to all upper case letters
13007          * @param {String} value The text to convert
13008          * @return {String} The converted text
13009          */
13010         uppercase : function(value){
13011             return String(value).toUpperCase();
13012         },
13013
13014         /**
13015          * Converts the first character only of a string to upper case
13016          * @param {String} value The text to convert
13017          * @return {String} The converted text
13018          */
13019         capitalize : function(value){
13020             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13021         },
13022
13023         // private
13024         call : function(value, fn){
13025             if(arguments.length > 2){
13026                 var args = Array.prototype.slice.call(arguments, 2);
13027                 args.unshift(value);
13028                  
13029                 return /** eval:var:value */  eval(fn).apply(window, args);
13030             }else{
13031                 /** eval:var:value */
13032                 return /** eval:var:value */ eval(fn).call(window, value);
13033             }
13034         },
13035
13036        
13037         /**
13038          * safer version of Math.toFixed..??/
13039          * @param {Number/String} value The numeric value to format
13040          * @param {Number/String} value Decimal places 
13041          * @return {String} The formatted currency string
13042          */
13043         toFixed : function(v, n)
13044         {
13045             // why not use to fixed - precision is buggered???
13046             if (!n) {
13047                 return Math.round(v-0);
13048             }
13049             var fact = Math.pow(10,n+1);
13050             v = (Math.round((v-0)*fact))/fact;
13051             var z = (''+fact).substring(2);
13052             if (v == Math.floor(v)) {
13053                 return Math.floor(v) + '.' + z;
13054             }
13055             
13056             // now just padd decimals..
13057             var ps = String(v).split('.');
13058             var fd = (ps[1] + z);
13059             var r = fd.substring(0,n); 
13060             var rm = fd.substring(n); 
13061             if (rm < 5) {
13062                 return ps[0] + '.' + r;
13063             }
13064             r*=1; // turn it into a number;
13065             r++;
13066             if (String(r).length != n) {
13067                 ps[0]*=1;
13068                 ps[0]++;
13069                 r = String(r).substring(1); // chop the end off.
13070             }
13071             
13072             return ps[0] + '.' + r;
13073              
13074         },
13075         
13076         /**
13077          * Format a number as US currency
13078          * @param {Number/String} value The numeric value to format
13079          * @return {String} The formatted currency string
13080          */
13081         usMoney : function(v){
13082             v = (Math.round((v-0)*100))/100;
13083             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13084             v = String(v);
13085             var ps = v.split('.');
13086             var whole = ps[0];
13087             var sub = ps[1] ? '.'+ ps[1] : '.00';
13088             var r = /(\d+)(\d{3})/;
13089             while (r.test(whole)) {
13090                 whole = whole.replace(r, '$1' + ',' + '$2');
13091             }
13092             return "$" + whole + sub ;
13093         },
13094         
13095         /**
13096          * Parse a value into a formatted date using the specified format pattern.
13097          * @param {Mixed} value The value to format
13098          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13099          * @return {String} The formatted date string
13100          */
13101         date : function(v, format){
13102             if(!v){
13103                 return "";
13104             }
13105             if(!(v instanceof Date)){
13106                 v = new Date(Date.parse(v));
13107             }
13108             return v.dateFormat(format || "m/d/Y");
13109         },
13110
13111         /**
13112          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13113          * @param {String} format Any valid date format string
13114          * @return {Function} The date formatting function
13115          */
13116         dateRenderer : function(format){
13117             return function(v){
13118                 return Roo.util.Format.date(v, format);  
13119             };
13120         },
13121
13122         // private
13123         stripTagsRE : /<\/?[^>]+>/gi,
13124         
13125         /**
13126          * Strips all HTML tags
13127          * @param {Mixed} value The text from which to strip tags
13128          * @return {String} The stripped text
13129          */
13130         stripTags : function(v){
13131             return !v ? v : String(v).replace(this.stripTagsRE, "");
13132         }
13133     };
13134 }();/*
13135  * Based on:
13136  * Ext JS Library 1.1.1
13137  * Copyright(c) 2006-2007, Ext JS, LLC.
13138  *
13139  * Originally Released Under LGPL - original licence link has changed is not relivant.
13140  *
13141  * Fork - LGPL
13142  * <script type="text/javascript">
13143  */
13144
13145
13146  
13147
13148 /**
13149  * @class Roo.MasterTemplate
13150  * @extends Roo.Template
13151  * Provides a template that can have child templates. The syntax is:
13152 <pre><code>
13153 var t = new Roo.MasterTemplate(
13154         '&lt;select name="{name}"&gt;',
13155                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13156         '&lt;/select&gt;'
13157 );
13158 t.add('options', {value: 'foo', text: 'bar'});
13159 // or you can add multiple child elements in one shot
13160 t.addAll('options', [
13161     {value: 'foo', text: 'bar'},
13162     {value: 'foo2', text: 'bar2'},
13163     {value: 'foo3', text: 'bar3'}
13164 ]);
13165 // then append, applying the master template values
13166 t.append('my-form', {name: 'my-select'});
13167 </code></pre>
13168 * A name attribute for the child template is not required if you have only one child
13169 * template or you want to refer to them by index.
13170  */
13171 Roo.MasterTemplate = function(){
13172     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13173     this.originalHtml = this.html;
13174     var st = {};
13175     var m, re = this.subTemplateRe;
13176     re.lastIndex = 0;
13177     var subIndex = 0;
13178     while(m = re.exec(this.html)){
13179         var name = m[1], content = m[2];
13180         st[subIndex] = {
13181             name: name,
13182             index: subIndex,
13183             buffer: [],
13184             tpl : new Roo.Template(content)
13185         };
13186         if(name){
13187             st[name] = st[subIndex];
13188         }
13189         st[subIndex].tpl.compile();
13190         st[subIndex].tpl.call = this.call.createDelegate(this);
13191         subIndex++;
13192     }
13193     this.subCount = subIndex;
13194     this.subs = st;
13195 };
13196 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13197     /**
13198     * The regular expression used to match sub templates
13199     * @type RegExp
13200     * @property
13201     */
13202     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13203
13204     /**
13205      * Applies the passed values to a child template.
13206      * @param {String/Number} name (optional) The name or index of the child template
13207      * @param {Array/Object} values The values to be applied to the template
13208      * @return {MasterTemplate} this
13209      */
13210      add : function(name, values){
13211         if(arguments.length == 1){
13212             values = arguments[0];
13213             name = 0;
13214         }
13215         var s = this.subs[name];
13216         s.buffer[s.buffer.length] = s.tpl.apply(values);
13217         return this;
13218     },
13219
13220     /**
13221      * Applies all the passed values to a child template.
13222      * @param {String/Number} name (optional) The name or index of the child template
13223      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13224      * @param {Boolean} reset (optional) True to reset the template first
13225      * @return {MasterTemplate} this
13226      */
13227     fill : function(name, values, reset){
13228         var a = arguments;
13229         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13230             values = a[0];
13231             name = 0;
13232             reset = a[1];
13233         }
13234         if(reset){
13235             this.reset();
13236         }
13237         for(var i = 0, len = values.length; i < len; i++){
13238             this.add(name, values[i]);
13239         }
13240         return this;
13241     },
13242
13243     /**
13244      * Resets the template for reuse
13245      * @return {MasterTemplate} this
13246      */
13247      reset : function(){
13248         var s = this.subs;
13249         for(var i = 0; i < this.subCount; i++){
13250             s[i].buffer = [];
13251         }
13252         return this;
13253     },
13254
13255     applyTemplate : function(values){
13256         var s = this.subs;
13257         var replaceIndex = -1;
13258         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13259             return s[++replaceIndex].buffer.join("");
13260         });
13261         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13262     },
13263
13264     apply : function(){
13265         return this.applyTemplate.apply(this, arguments);
13266     },
13267
13268     compile : function(){return this;}
13269 });
13270
13271 /**
13272  * Alias for fill().
13273  * @method
13274  */
13275 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13276  /**
13277  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13278  * var tpl = Roo.MasterTemplate.from('element-id');
13279  * @param {String/HTMLElement} el
13280  * @param {Object} config
13281  * @static
13282  */
13283 Roo.MasterTemplate.from = function(el, config){
13284     el = Roo.getDom(el);
13285     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13286 };/*
13287  * Based on:
13288  * Ext JS Library 1.1.1
13289  * Copyright(c) 2006-2007, Ext JS, LLC.
13290  *
13291  * Originally Released Under LGPL - original licence link has changed is not relivant.
13292  *
13293  * Fork - LGPL
13294  * <script type="text/javascript">
13295  */
13296
13297  
13298 /**
13299  * @class Roo.util.CSS
13300  * Utility class for manipulating CSS rules
13301  * @singleton
13302  */
13303 Roo.util.CSS = function(){
13304         var rules = null;
13305         var doc = document;
13306
13307     var camelRe = /(-[a-z])/gi;
13308     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13309
13310    return {
13311    /**
13312     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13313     * tag and appended to the HEAD of the document.
13314     * @param {String|Object} cssText The text containing the css rules
13315     * @param {String} id An id to add to the stylesheet for later removal
13316     * @return {StyleSheet}
13317     */
13318     createStyleSheet : function(cssText, id){
13319         var ss;
13320         var head = doc.getElementsByTagName("head")[0];
13321         var nrules = doc.createElement("style");
13322         nrules.setAttribute("type", "text/css");
13323         if(id){
13324             nrules.setAttribute("id", id);
13325         }
13326         if (typeof(cssText) != 'string') {
13327             // support object maps..
13328             // not sure if this a good idea.. 
13329             // perhaps it should be merged with the general css handling
13330             // and handle js style props.
13331             var cssTextNew = [];
13332             for(var n in cssText) {
13333                 var citems = [];
13334                 for(var k in cssText[n]) {
13335                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13336                 }
13337                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13338                 
13339             }
13340             cssText = cssTextNew.join("\n");
13341             
13342         }
13343        
13344        
13345        if(Roo.isIE){
13346            head.appendChild(nrules);
13347            ss = nrules.styleSheet;
13348            ss.cssText = cssText;
13349        }else{
13350            try{
13351                 nrules.appendChild(doc.createTextNode(cssText));
13352            }catch(e){
13353                nrules.cssText = cssText; 
13354            }
13355            head.appendChild(nrules);
13356            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13357        }
13358        this.cacheStyleSheet(ss);
13359        return ss;
13360    },
13361
13362    /**
13363     * Removes a style or link tag by id
13364     * @param {String} id The id of the tag
13365     */
13366    removeStyleSheet : function(id){
13367        var existing = doc.getElementById(id);
13368        if(existing){
13369            existing.parentNode.removeChild(existing);
13370        }
13371    },
13372
13373    /**
13374     * Dynamically swaps an existing stylesheet reference for a new one
13375     * @param {String} id The id of an existing link tag to remove
13376     * @param {String} url The href of the new stylesheet to include
13377     */
13378    swapStyleSheet : function(id, url){
13379        this.removeStyleSheet(id);
13380        var ss = doc.createElement("link");
13381        ss.setAttribute("rel", "stylesheet");
13382        ss.setAttribute("type", "text/css");
13383        ss.setAttribute("id", id);
13384        ss.setAttribute("href", url);
13385        doc.getElementsByTagName("head")[0].appendChild(ss);
13386    },
13387    
13388    /**
13389     * Refresh the rule cache if you have dynamically added stylesheets
13390     * @return {Object} An object (hash) of rules indexed by selector
13391     */
13392    refreshCache : function(){
13393        return this.getRules(true);
13394    },
13395
13396    // private
13397    cacheStyleSheet : function(stylesheet){
13398        if(!rules){
13399            rules = {};
13400        }
13401        try{// try catch for cross domain access issue
13402            var ssRules = stylesheet.cssRules || stylesheet.rules;
13403            for(var j = ssRules.length-1; j >= 0; --j){
13404                rules[ssRules[j].selectorText] = ssRules[j];
13405            }
13406        }catch(e){}
13407    },
13408    
13409    /**
13410     * Gets all css rules for the document
13411     * @param {Boolean} refreshCache true to refresh the internal cache
13412     * @return {Object} An object (hash) of rules indexed by selector
13413     */
13414    getRules : function(refreshCache){
13415                 if(rules == null || refreshCache){
13416                         rules = {};
13417                         var ds = doc.styleSheets;
13418                         for(var i =0, len = ds.length; i < len; i++){
13419                             try{
13420                         this.cacheStyleSheet(ds[i]);
13421                     }catch(e){} 
13422                 }
13423                 }
13424                 return rules;
13425         },
13426         
13427         /**
13428     * Gets an an individual CSS rule by selector(s)
13429     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13430     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13431     * @return {CSSRule} The CSS rule or null if one is not found
13432     */
13433    getRule : function(selector, refreshCache){
13434                 var rs = this.getRules(refreshCache);
13435                 if(!(selector instanceof Array)){
13436                     return rs[selector];
13437                 }
13438                 for(var i = 0; i < selector.length; i++){
13439                         if(rs[selector[i]]){
13440                                 return rs[selector[i]];
13441                         }
13442                 }
13443                 return null;
13444         },
13445         
13446         
13447         /**
13448     * Updates a rule property
13449     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13450     * @param {String} property The css property
13451     * @param {String} value The new value for the property
13452     * @return {Boolean} true If a rule was found and updated
13453     */
13454    updateRule : function(selector, property, value){
13455                 if(!(selector instanceof Array)){
13456                         var rule = this.getRule(selector);
13457                         if(rule){
13458                                 rule.style[property.replace(camelRe, camelFn)] = value;
13459                                 return true;
13460                         }
13461                 }else{
13462                         for(var i = 0; i < selector.length; i++){
13463                                 if(this.updateRule(selector[i], property, value)){
13464                                         return true;
13465                                 }
13466                         }
13467                 }
13468                 return false;
13469         }
13470    };   
13471 }();/*
13472  * Based on:
13473  * Ext JS Library 1.1.1
13474  * Copyright(c) 2006-2007, Ext JS, LLC.
13475  *
13476  * Originally Released Under LGPL - original licence link has changed is not relivant.
13477  *
13478  * Fork - LGPL
13479  * <script type="text/javascript">
13480  */
13481
13482  
13483
13484 /**
13485  * @class Roo.util.ClickRepeater
13486  * @extends Roo.util.Observable
13487  * 
13488  * A wrapper class which can be applied to any element. Fires a "click" event while the
13489  * mouse is pressed. The interval between firings may be specified in the config but
13490  * defaults to 10 milliseconds.
13491  * 
13492  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13493  * 
13494  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13495  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13496  * Similar to an autorepeat key delay.
13497  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13498  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13499  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13500  *           "interval" and "delay" are ignored. "immediate" is honored.
13501  * @cfg {Boolean} preventDefault True to prevent the default click event
13502  * @cfg {Boolean} stopDefault True to stop the default click event
13503  * 
13504  * @history
13505  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13506  *     2007-02-02 jvs Renamed to ClickRepeater
13507  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13508  *
13509  *  @constructor
13510  * @param {String/HTMLElement/Element} el The element to listen on
13511  * @param {Object} config
13512  **/
13513 Roo.util.ClickRepeater = function(el, config)
13514 {
13515     this.el = Roo.get(el);
13516     this.el.unselectable();
13517
13518     Roo.apply(this, config);
13519
13520     this.addEvents({
13521     /**
13522      * @event mousedown
13523      * Fires when the mouse button is depressed.
13524      * @param {Roo.util.ClickRepeater} this
13525      */
13526         "mousedown" : true,
13527     /**
13528      * @event click
13529      * Fires on a specified interval during the time the element is pressed.
13530      * @param {Roo.util.ClickRepeater} this
13531      */
13532         "click" : true,
13533     /**
13534      * @event mouseup
13535      * Fires when the mouse key is released.
13536      * @param {Roo.util.ClickRepeater} this
13537      */
13538         "mouseup" : true
13539     });
13540
13541     this.el.on("mousedown", this.handleMouseDown, this);
13542     if(this.preventDefault || this.stopDefault){
13543         this.el.on("click", function(e){
13544             if(this.preventDefault){
13545                 e.preventDefault();
13546             }
13547             if(this.stopDefault){
13548                 e.stopEvent();
13549             }
13550         }, this);
13551     }
13552
13553     // allow inline handler
13554     if(this.handler){
13555         this.on("click", this.handler,  this.scope || this);
13556     }
13557
13558     Roo.util.ClickRepeater.superclass.constructor.call(this);
13559 };
13560
13561 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13562     interval : 20,
13563     delay: 250,
13564     preventDefault : true,
13565     stopDefault : false,
13566     timer : 0,
13567
13568     // private
13569     handleMouseDown : function(){
13570         clearTimeout(this.timer);
13571         this.el.blur();
13572         if(this.pressClass){
13573             this.el.addClass(this.pressClass);
13574         }
13575         this.mousedownTime = new Date();
13576
13577         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13578         this.el.on("mouseout", this.handleMouseOut, this);
13579
13580         this.fireEvent("mousedown", this);
13581         this.fireEvent("click", this);
13582         
13583         this.timer = this.click.defer(this.delay || this.interval, this);
13584     },
13585
13586     // private
13587     click : function(){
13588         this.fireEvent("click", this);
13589         this.timer = this.click.defer(this.getInterval(), this);
13590     },
13591
13592     // private
13593     getInterval: function(){
13594         if(!this.accelerate){
13595             return this.interval;
13596         }
13597         var pressTime = this.mousedownTime.getElapsed();
13598         if(pressTime < 500){
13599             return 400;
13600         }else if(pressTime < 1700){
13601             return 320;
13602         }else if(pressTime < 2600){
13603             return 250;
13604         }else if(pressTime < 3500){
13605             return 180;
13606         }else if(pressTime < 4400){
13607             return 140;
13608         }else if(pressTime < 5300){
13609             return 80;
13610         }else if(pressTime < 6200){
13611             return 50;
13612         }else{
13613             return 10;
13614         }
13615     },
13616
13617     // private
13618     handleMouseOut : function(){
13619         clearTimeout(this.timer);
13620         if(this.pressClass){
13621             this.el.removeClass(this.pressClass);
13622         }
13623         this.el.on("mouseover", this.handleMouseReturn, this);
13624     },
13625
13626     // private
13627     handleMouseReturn : function(){
13628         this.el.un("mouseover", this.handleMouseReturn);
13629         if(this.pressClass){
13630             this.el.addClass(this.pressClass);
13631         }
13632         this.click();
13633     },
13634
13635     // private
13636     handleMouseUp : function(){
13637         clearTimeout(this.timer);
13638         this.el.un("mouseover", this.handleMouseReturn);
13639         this.el.un("mouseout", this.handleMouseOut);
13640         Roo.get(document).un("mouseup", this.handleMouseUp);
13641         this.el.removeClass(this.pressClass);
13642         this.fireEvent("mouseup", this);
13643     }
13644 });/*
13645  * Based on:
13646  * Ext JS Library 1.1.1
13647  * Copyright(c) 2006-2007, Ext JS, LLC.
13648  *
13649  * Originally Released Under LGPL - original licence link has changed is not relivant.
13650  *
13651  * Fork - LGPL
13652  * <script type="text/javascript">
13653  */
13654
13655  
13656 /**
13657  * @class Roo.KeyNav
13658  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13659  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13660  * way to implement custom navigation schemes for any UI component.</p>
13661  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13662  * pageUp, pageDown, del, home, end.  Usage:</p>
13663  <pre><code>
13664 var nav = new Roo.KeyNav("my-element", {
13665     "left" : function(e){
13666         this.moveLeft(e.ctrlKey);
13667     },
13668     "right" : function(e){
13669         this.moveRight(e.ctrlKey);
13670     },
13671     "enter" : function(e){
13672         this.save();
13673     },
13674     scope : this
13675 });
13676 </code></pre>
13677  * @constructor
13678  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13679  * @param {Object} config The config
13680  */
13681 Roo.KeyNav = function(el, config){
13682     this.el = Roo.get(el);
13683     Roo.apply(this, config);
13684     if(!this.disabled){
13685         this.disabled = true;
13686         this.enable();
13687     }
13688 };
13689
13690 Roo.KeyNav.prototype = {
13691     /**
13692      * @cfg {Boolean} disabled
13693      * True to disable this KeyNav instance (defaults to false)
13694      */
13695     disabled : false,
13696     /**
13697      * @cfg {String} defaultEventAction
13698      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13699      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13700      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13701      */
13702     defaultEventAction: "stopEvent",
13703     /**
13704      * @cfg {Boolean} forceKeyDown
13705      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13706      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13707      * handle keydown instead of keypress.
13708      */
13709     forceKeyDown : false,
13710
13711     // private
13712     prepareEvent : function(e){
13713         var k = e.getKey();
13714         var h = this.keyToHandler[k];
13715         //if(h && this[h]){
13716         //    e.stopPropagation();
13717         //}
13718         if(Roo.isSafari && h && k >= 37 && k <= 40){
13719             e.stopEvent();
13720         }
13721     },
13722
13723     // private
13724     relay : function(e){
13725         var k = e.getKey();
13726         var h = this.keyToHandler[k];
13727         if(h && this[h]){
13728             if(this.doRelay(e, this[h], h) !== true){
13729                 e[this.defaultEventAction]();
13730             }
13731         }
13732     },
13733
13734     // private
13735     doRelay : function(e, h, hname){
13736         return h.call(this.scope || this, e);
13737     },
13738
13739     // possible handlers
13740     enter : false,
13741     left : false,
13742     right : false,
13743     up : false,
13744     down : false,
13745     tab : false,
13746     esc : false,
13747     pageUp : false,
13748     pageDown : false,
13749     del : false,
13750     home : false,
13751     end : false,
13752
13753     // quick lookup hash
13754     keyToHandler : {
13755         37 : "left",
13756         39 : "right",
13757         38 : "up",
13758         40 : "down",
13759         33 : "pageUp",
13760         34 : "pageDown",
13761         46 : "del",
13762         36 : "home",
13763         35 : "end",
13764         13 : "enter",
13765         27 : "esc",
13766         9  : "tab"
13767     },
13768
13769         /**
13770          * Enable this KeyNav
13771          */
13772         enable: function(){
13773                 if(this.disabled){
13774             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13775             // the EventObject will normalize Safari automatically
13776             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13777                 this.el.on("keydown", this.relay,  this);
13778             }else{
13779                 this.el.on("keydown", this.prepareEvent,  this);
13780                 this.el.on("keypress", this.relay,  this);
13781             }
13782                     this.disabled = false;
13783                 }
13784         },
13785
13786         /**
13787          * Disable this KeyNav
13788          */
13789         disable: function(){
13790                 if(!this.disabled){
13791                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13792                 this.el.un("keydown", this.relay);
13793             }else{
13794                 this.el.un("keydown", this.prepareEvent);
13795                 this.el.un("keypress", this.relay);
13796             }
13797                     this.disabled = true;
13798                 }
13799         }
13800 };/*
13801  * Based on:
13802  * Ext JS Library 1.1.1
13803  * Copyright(c) 2006-2007, Ext JS, LLC.
13804  *
13805  * Originally Released Under LGPL - original licence link has changed is not relivant.
13806  *
13807  * Fork - LGPL
13808  * <script type="text/javascript">
13809  */
13810
13811  
13812 /**
13813  * @class Roo.KeyMap
13814  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13815  * The constructor accepts the same config object as defined by {@link #addBinding}.
13816  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13817  * combination it will call the function with this signature (if the match is a multi-key
13818  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13819  * A KeyMap can also handle a string representation of keys.<br />
13820  * Usage:
13821  <pre><code>
13822 // map one key by key code
13823 var map = new Roo.KeyMap("my-element", {
13824     key: 13, // or Roo.EventObject.ENTER
13825     fn: myHandler,
13826     scope: myObject
13827 });
13828
13829 // map multiple keys to one action by string
13830 var map = new Roo.KeyMap("my-element", {
13831     key: "a\r\n\t",
13832     fn: myHandler,
13833     scope: myObject
13834 });
13835
13836 // map multiple keys to multiple actions by strings and array of codes
13837 var map = new Roo.KeyMap("my-element", [
13838     {
13839         key: [10,13],
13840         fn: function(){ alert("Return was pressed"); }
13841     }, {
13842         key: "abc",
13843         fn: function(){ alert('a, b or c was pressed'); }
13844     }, {
13845         key: "\t",
13846         ctrl:true,
13847         shift:true,
13848         fn: function(){ alert('Control + shift + tab was pressed.'); }
13849     }
13850 ]);
13851 </code></pre>
13852  * <b>Note: A KeyMap starts enabled</b>
13853  * @constructor
13854  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13855  * @param {Object} config The config (see {@link #addBinding})
13856  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13857  */
13858 Roo.KeyMap = function(el, config, eventName){
13859     this.el  = Roo.get(el);
13860     this.eventName = eventName || "keydown";
13861     this.bindings = [];
13862     if(config){
13863         this.addBinding(config);
13864     }
13865     this.enable();
13866 };
13867
13868 Roo.KeyMap.prototype = {
13869     /**
13870      * True to stop the event from bubbling and prevent the default browser action if the
13871      * key was handled by the KeyMap (defaults to false)
13872      * @type Boolean
13873      */
13874     stopEvent : false,
13875
13876     /**
13877      * Add a new binding to this KeyMap. The following config object properties are supported:
13878      * <pre>
13879 Property    Type             Description
13880 ----------  ---------------  ----------------------------------------------------------------------
13881 key         String/Array     A single keycode or an array of keycodes to handle
13882 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13883 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13884 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13885 fn          Function         The function to call when KeyMap finds the expected key combination
13886 scope       Object           The scope of the callback function
13887 </pre>
13888      *
13889      * Usage:
13890      * <pre><code>
13891 // Create a KeyMap
13892 var map = new Roo.KeyMap(document, {
13893     key: Roo.EventObject.ENTER,
13894     fn: handleKey,
13895     scope: this
13896 });
13897
13898 //Add a new binding to the existing KeyMap later
13899 map.addBinding({
13900     key: 'abc',
13901     shift: true,
13902     fn: handleKey,
13903     scope: this
13904 });
13905 </code></pre>
13906      * @param {Object/Array} config A single KeyMap config or an array of configs
13907      */
13908         addBinding : function(config){
13909         if(config instanceof Array){
13910             for(var i = 0, len = config.length; i < len; i++){
13911                 this.addBinding(config[i]);
13912             }
13913             return;
13914         }
13915         var keyCode = config.key,
13916             shift = config.shift, 
13917             ctrl = config.ctrl, 
13918             alt = config.alt,
13919             fn = config.fn,
13920             scope = config.scope;
13921         if(typeof keyCode == "string"){
13922             var ks = [];
13923             var keyString = keyCode.toUpperCase();
13924             for(var j = 0, len = keyString.length; j < len; j++){
13925                 ks.push(keyString.charCodeAt(j));
13926             }
13927             keyCode = ks;
13928         }
13929         var keyArray = keyCode instanceof Array;
13930         var handler = function(e){
13931             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13932                 var k = e.getKey();
13933                 if(keyArray){
13934                     for(var i = 0, len = keyCode.length; i < len; i++){
13935                         if(keyCode[i] == k){
13936                           if(this.stopEvent){
13937                               e.stopEvent();
13938                           }
13939                           fn.call(scope || window, k, e);
13940                           return;
13941                         }
13942                     }
13943                 }else{
13944                     if(k == keyCode){
13945                         if(this.stopEvent){
13946                            e.stopEvent();
13947                         }
13948                         fn.call(scope || window, k, e);
13949                     }
13950                 }
13951             }
13952         };
13953         this.bindings.push(handler);  
13954         },
13955
13956     /**
13957      * Shorthand for adding a single key listener
13958      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13959      * following options:
13960      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13961      * @param {Function} fn The function to call
13962      * @param {Object} scope (optional) The scope of the function
13963      */
13964     on : function(key, fn, scope){
13965         var keyCode, shift, ctrl, alt;
13966         if(typeof key == "object" && !(key instanceof Array)){
13967             keyCode = key.key;
13968             shift = key.shift;
13969             ctrl = key.ctrl;
13970             alt = key.alt;
13971         }else{
13972             keyCode = key;
13973         }
13974         this.addBinding({
13975             key: keyCode,
13976             shift: shift,
13977             ctrl: ctrl,
13978             alt: alt,
13979             fn: fn,
13980             scope: scope
13981         })
13982     },
13983
13984     // private
13985     handleKeyDown : function(e){
13986             if(this.enabled){ //just in case
13987             var b = this.bindings;
13988             for(var i = 0, len = b.length; i < len; i++){
13989                 b[i].call(this, e);
13990             }
13991             }
13992         },
13993         
13994         /**
13995          * Returns true if this KeyMap is enabled
13996          * @return {Boolean} 
13997          */
13998         isEnabled : function(){
13999             return this.enabled;  
14000         },
14001         
14002         /**
14003          * Enables this KeyMap
14004          */
14005         enable: function(){
14006                 if(!this.enabled){
14007                     this.el.on(this.eventName, this.handleKeyDown, this);
14008                     this.enabled = true;
14009                 }
14010         },
14011
14012         /**
14013          * Disable this KeyMap
14014          */
14015         disable: function(){
14016                 if(this.enabled){
14017                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14018                     this.enabled = false;
14019                 }
14020         }
14021 };/*
14022  * Based on:
14023  * Ext JS Library 1.1.1
14024  * Copyright(c) 2006-2007, Ext JS, LLC.
14025  *
14026  * Originally Released Under LGPL - original licence link has changed is not relivant.
14027  *
14028  * Fork - LGPL
14029  * <script type="text/javascript">
14030  */
14031
14032  
14033 /**
14034  * @class Roo.util.TextMetrics
14035  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14036  * wide, in pixels, a given block of text will be.
14037  * @singleton
14038  */
14039 Roo.util.TextMetrics = function(){
14040     var shared;
14041     return {
14042         /**
14043          * Measures the size of the specified text
14044          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14045          * that can affect the size of the rendered text
14046          * @param {String} text The text to measure
14047          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14048          * in order to accurately measure the text height
14049          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14050          */
14051         measure : function(el, text, fixedWidth){
14052             if(!shared){
14053                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14054             }
14055             shared.bind(el);
14056             shared.setFixedWidth(fixedWidth || 'auto');
14057             return shared.getSize(text);
14058         },
14059
14060         /**
14061          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14062          * the overhead of multiple calls to initialize the style properties on each measurement.
14063          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14064          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14065          * in order to accurately measure the text height
14066          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14067          */
14068         createInstance : function(el, fixedWidth){
14069             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14070         }
14071     };
14072 }();
14073
14074  
14075
14076 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14077     var ml = new Roo.Element(document.createElement('div'));
14078     document.body.appendChild(ml.dom);
14079     ml.position('absolute');
14080     ml.setLeftTop(-1000, -1000);
14081     ml.hide();
14082
14083     if(fixedWidth){
14084         ml.setWidth(fixedWidth);
14085     }
14086      
14087     var instance = {
14088         /**
14089          * Returns the size of the specified text based on the internal element's style and width properties
14090          * @memberOf Roo.util.TextMetrics.Instance#
14091          * @param {String} text The text to measure
14092          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14093          */
14094         getSize : function(text){
14095             ml.update(text);
14096             var s = ml.getSize();
14097             ml.update('');
14098             return s;
14099         },
14100
14101         /**
14102          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14103          * that can affect the size of the rendered text
14104          * @memberOf Roo.util.TextMetrics.Instance#
14105          * @param {String/HTMLElement} el The element, dom node or id
14106          */
14107         bind : function(el){
14108             ml.setStyle(
14109                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14110             );
14111         },
14112
14113         /**
14114          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14115          * to set a fixed width in order to accurately measure the text height.
14116          * @memberOf Roo.util.TextMetrics.Instance#
14117          * @param {Number} width The width to set on the element
14118          */
14119         setFixedWidth : function(width){
14120             ml.setWidth(width);
14121         },
14122
14123         /**
14124          * Returns the measured width of the specified text
14125          * @memberOf Roo.util.TextMetrics.Instance#
14126          * @param {String} text The text to measure
14127          * @return {Number} width The width in pixels
14128          */
14129         getWidth : function(text){
14130             ml.dom.style.width = 'auto';
14131             return this.getSize(text).width;
14132         },
14133
14134         /**
14135          * Returns the measured height of the specified text.  For multiline text, be sure to call
14136          * {@link #setFixedWidth} if necessary.
14137          * @memberOf Roo.util.TextMetrics.Instance#
14138          * @param {String} text The text to measure
14139          * @return {Number} height The height in pixels
14140          */
14141         getHeight : function(text){
14142             return this.getSize(text).height;
14143         }
14144     };
14145
14146     instance.bind(bindTo);
14147
14148     return instance;
14149 };
14150
14151 // backwards compat
14152 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14153  * Based on:
14154  * Ext JS Library 1.1.1
14155  * Copyright(c) 2006-2007, Ext JS, LLC.
14156  *
14157  * Originally Released Under LGPL - original licence link has changed is not relivant.
14158  *
14159  * Fork - LGPL
14160  * <script type="text/javascript">
14161  */
14162
14163 /**
14164  * @class Roo.state.Provider
14165  * Abstract base class for state provider implementations. This class provides methods
14166  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14167  * Provider interface.
14168  */
14169 Roo.state.Provider = function(){
14170     /**
14171      * @event statechange
14172      * Fires when a state change occurs.
14173      * @param {Provider} this This state provider
14174      * @param {String} key The state key which was changed
14175      * @param {String} value The encoded value for the state
14176      */
14177     this.addEvents({
14178         "statechange": true
14179     });
14180     this.state = {};
14181     Roo.state.Provider.superclass.constructor.call(this);
14182 };
14183 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14184     /**
14185      * Returns the current value for a key
14186      * @param {String} name The key name
14187      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14188      * @return {Mixed} The state data
14189      */
14190     get : function(name, defaultValue){
14191         return typeof this.state[name] == "undefined" ?
14192             defaultValue : this.state[name];
14193     },
14194     
14195     /**
14196      * Clears a value from the state
14197      * @param {String} name The key name
14198      */
14199     clear : function(name){
14200         delete this.state[name];
14201         this.fireEvent("statechange", this, name, null);
14202     },
14203     
14204     /**
14205      * Sets the value for a key
14206      * @param {String} name The key name
14207      * @param {Mixed} value The value to set
14208      */
14209     set : function(name, value){
14210         this.state[name] = value;
14211         this.fireEvent("statechange", this, name, value);
14212     },
14213     
14214     /**
14215      * Decodes a string previously encoded with {@link #encodeValue}.
14216      * @param {String} value The value to decode
14217      * @return {Mixed} The decoded value
14218      */
14219     decodeValue : function(cookie){
14220         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14221         var matches = re.exec(unescape(cookie));
14222         if(!matches || !matches[1]) return; // non state cookie
14223         var type = matches[1];
14224         var v = matches[2];
14225         switch(type){
14226             case "n":
14227                 return parseFloat(v);
14228             case "d":
14229                 return new Date(Date.parse(v));
14230             case "b":
14231                 return (v == "1");
14232             case "a":
14233                 var all = [];
14234                 var values = v.split("^");
14235                 for(var i = 0, len = values.length; i < len; i++){
14236                     all.push(this.decodeValue(values[i]));
14237                 }
14238                 return all;
14239            case "o":
14240                 var all = {};
14241                 var values = v.split("^");
14242                 for(var i = 0, len = values.length; i < len; i++){
14243                     var kv = values[i].split("=");
14244                     all[kv[0]] = this.decodeValue(kv[1]);
14245                 }
14246                 return all;
14247            default:
14248                 return v;
14249         }
14250     },
14251     
14252     /**
14253      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14254      * @param {Mixed} value The value to encode
14255      * @return {String} The encoded value
14256      */
14257     encodeValue : function(v){
14258         var enc;
14259         if(typeof v == "number"){
14260             enc = "n:" + v;
14261         }else if(typeof v == "boolean"){
14262             enc = "b:" + (v ? "1" : "0");
14263         }else if(v instanceof Date){
14264             enc = "d:" + v.toGMTString();
14265         }else if(v instanceof Array){
14266             var flat = "";
14267             for(var i = 0, len = v.length; i < len; i++){
14268                 flat += this.encodeValue(v[i]);
14269                 if(i != len-1) flat += "^";
14270             }
14271             enc = "a:" + flat;
14272         }else if(typeof v == "object"){
14273             var flat = "";
14274             for(var key in v){
14275                 if(typeof v[key] != "function"){
14276                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14277                 }
14278             }
14279             enc = "o:" + flat.substring(0, flat.length-1);
14280         }else{
14281             enc = "s:" + v;
14282         }
14283         return escape(enc);        
14284     }
14285 });
14286
14287 /*
14288  * Based on:
14289  * Ext JS Library 1.1.1
14290  * Copyright(c) 2006-2007, Ext JS, LLC.
14291  *
14292  * Originally Released Under LGPL - original licence link has changed is not relivant.
14293  *
14294  * Fork - LGPL
14295  * <script type="text/javascript">
14296  */
14297 /**
14298  * @class Roo.state.Manager
14299  * This is the global state manager. By default all components that are "state aware" check this class
14300  * for state information if you don't pass them a custom state provider. In order for this class
14301  * to be useful, it must be initialized with a provider when your application initializes.
14302  <pre><code>
14303 // in your initialization function
14304 init : function(){
14305    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14306    ...
14307    // supposed you have a {@link Roo.BorderLayout}
14308    var layout = new Roo.BorderLayout(...);
14309    layout.restoreState();
14310    // or a {Roo.BasicDialog}
14311    var dialog = new Roo.BasicDialog(...);
14312    dialog.restoreState();
14313  </code></pre>
14314  * @singleton
14315  */
14316 Roo.state.Manager = function(){
14317     var provider = new Roo.state.Provider();
14318     
14319     return {
14320         /**
14321          * Configures the default state provider for your application
14322          * @param {Provider} stateProvider The state provider to set
14323          */
14324         setProvider : function(stateProvider){
14325             provider = stateProvider;
14326         },
14327         
14328         /**
14329          * Returns the current value for a key
14330          * @param {String} name The key name
14331          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14332          * @return {Mixed} The state data
14333          */
14334         get : function(key, defaultValue){
14335             return provider.get(key, defaultValue);
14336         },
14337         
14338         /**
14339          * Sets the value for a key
14340          * @param {String} name The key name
14341          * @param {Mixed} value The state data
14342          */
14343          set : function(key, value){
14344             provider.set(key, value);
14345         },
14346         
14347         /**
14348          * Clears a value from the state
14349          * @param {String} name The key name
14350          */
14351         clear : function(key){
14352             provider.clear(key);
14353         },
14354         
14355         /**
14356          * Gets the currently configured state provider
14357          * @return {Provider} The state provider
14358          */
14359         getProvider : function(){
14360             return provider;
14361         }
14362     };
14363 }();
14364 /*
14365  * Based on:
14366  * Ext JS Library 1.1.1
14367  * Copyright(c) 2006-2007, Ext JS, LLC.
14368  *
14369  * Originally Released Under LGPL - original licence link has changed is not relivant.
14370  *
14371  * Fork - LGPL
14372  * <script type="text/javascript">
14373  */
14374 /**
14375  * @class Roo.state.CookieProvider
14376  * @extends Roo.state.Provider
14377  * The default Provider implementation which saves state via cookies.
14378  * <br />Usage:
14379  <pre><code>
14380    var cp = new Roo.state.CookieProvider({
14381        path: "/cgi-bin/",
14382        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14383        domain: "roojs.com"
14384    })
14385    Roo.state.Manager.setProvider(cp);
14386  </code></pre>
14387  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14388  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14389  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14390  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14391  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14392  * domain the page is running on including the 'www' like 'www.roojs.com')
14393  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14394  * @constructor
14395  * Create a new CookieProvider
14396  * @param {Object} config The configuration object
14397  */
14398 Roo.state.CookieProvider = function(config){
14399     Roo.state.CookieProvider.superclass.constructor.call(this);
14400     this.path = "/";
14401     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14402     this.domain = null;
14403     this.secure = false;
14404     Roo.apply(this, config);
14405     this.state = this.readCookies();
14406 };
14407
14408 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14409     // private
14410     set : function(name, value){
14411         if(typeof value == "undefined" || value === null){
14412             this.clear(name);
14413             return;
14414         }
14415         this.setCookie(name, value);
14416         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14417     },
14418
14419     // private
14420     clear : function(name){
14421         this.clearCookie(name);
14422         Roo.state.CookieProvider.superclass.clear.call(this, name);
14423     },
14424
14425     // private
14426     readCookies : function(){
14427         var cookies = {};
14428         var c = document.cookie + ";";
14429         var re = /\s?(.*?)=(.*?);/g;
14430         var matches;
14431         while((matches = re.exec(c)) != null){
14432             var name = matches[1];
14433             var value = matches[2];
14434             if(name && name.substring(0,3) == "ys-"){
14435                 cookies[name.substr(3)] = this.decodeValue(value);
14436             }
14437         }
14438         return cookies;
14439     },
14440
14441     // private
14442     setCookie : function(name, value){
14443         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14444            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14445            ((this.path == null) ? "" : ("; path=" + this.path)) +
14446            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14447            ((this.secure == true) ? "; secure" : "");
14448     },
14449
14450     // private
14451     clearCookie : function(name){
14452         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14453            ((this.path == null) ? "" : ("; path=" + this.path)) +
14454            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14455            ((this.secure == true) ? "; secure" : "");
14456     }
14457 });/*
14458  * Based on:
14459  * Ext JS Library 1.1.1
14460  * Copyright(c) 2006-2007, Ext JS, LLC.
14461  *
14462  * Originally Released Under LGPL - original licence link has changed is not relivant.
14463  *
14464  * Fork - LGPL
14465  * <script type="text/javascript">
14466  */
14467
14468
14469
14470 /*
14471  * These classes are derivatives of the similarly named classes in the YUI Library.
14472  * The original license:
14473  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14474  * Code licensed under the BSD License:
14475  * http://developer.yahoo.net/yui/license.txt
14476  */
14477
14478 (function() {
14479
14480 var Event=Roo.EventManager;
14481 var Dom=Roo.lib.Dom;
14482
14483 /**
14484  * @class Roo.dd.DragDrop
14485  * @extends Roo.util.Observable
14486  * Defines the interface and base operation of items that that can be
14487  * dragged or can be drop targets.  It was designed to be extended, overriding
14488  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14489  * Up to three html elements can be associated with a DragDrop instance:
14490  * <ul>
14491  * <li>linked element: the element that is passed into the constructor.
14492  * This is the element which defines the boundaries for interaction with
14493  * other DragDrop objects.</li>
14494  * <li>handle element(s): The drag operation only occurs if the element that
14495  * was clicked matches a handle element.  By default this is the linked
14496  * element, but there are times that you will want only a portion of the
14497  * linked element to initiate the drag operation, and the setHandleElId()
14498  * method provides a way to define this.</li>
14499  * <li>drag element: this represents the element that would be moved along
14500  * with the cursor during a drag operation.  By default, this is the linked
14501  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14502  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14503  * </li>
14504  * </ul>
14505  * This class should not be instantiated until the onload event to ensure that
14506  * the associated elements are available.
14507  * The following would define a DragDrop obj that would interact with any
14508  * other DragDrop obj in the "group1" group:
14509  * <pre>
14510  *  dd = new Roo.dd.DragDrop("div1", "group1");
14511  * </pre>
14512  * Since none of the event handlers have been implemented, nothing would
14513  * actually happen if you were to run the code above.  Normally you would
14514  * override this class or one of the default implementations, but you can
14515  * also override the methods you want on an instance of the class...
14516  * <pre>
14517  *  dd.onDragDrop = function(e, id) {
14518  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14519  *  }
14520  * </pre>
14521  * @constructor
14522  * @param {String} id of the element that is linked to this instance
14523  * @param {String} sGroup the group of related DragDrop objects
14524  * @param {object} config an object containing configurable attributes
14525  *                Valid properties for DragDrop:
14526  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14527  */
14528 Roo.dd.DragDrop = function(id, sGroup, config) {
14529     if (id) {
14530         this.init(id, sGroup, config);
14531     }
14532     
14533 };
14534
14535 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14536
14537     /**
14538      * The id of the element associated with this object.  This is what we
14539      * refer to as the "linked element" because the size and position of
14540      * this element is used to determine when the drag and drop objects have
14541      * interacted.
14542      * @property id
14543      * @type String
14544      */
14545     id: null,
14546
14547     /**
14548      * Configuration attributes passed into the constructor
14549      * @property config
14550      * @type object
14551      */
14552     config: null,
14553
14554     /**
14555      * The id of the element that will be dragged.  By default this is same
14556      * as the linked element , but could be changed to another element. Ex:
14557      * Roo.dd.DDProxy
14558      * @property dragElId
14559      * @type String
14560      * @private
14561      */
14562     dragElId: null,
14563
14564     /**
14565      * the id of the element that initiates the drag operation.  By default
14566      * this is the linked element, but could be changed to be a child of this
14567      * element.  This lets us do things like only starting the drag when the
14568      * header element within the linked html element is clicked.
14569      * @property handleElId
14570      * @type String
14571      * @private
14572      */
14573     handleElId: null,
14574
14575     /**
14576      * An associative array of HTML tags that will be ignored if clicked.
14577      * @property invalidHandleTypes
14578      * @type {string: string}
14579      */
14580     invalidHandleTypes: null,
14581
14582     /**
14583      * An associative array of ids for elements that will be ignored if clicked
14584      * @property invalidHandleIds
14585      * @type {string: string}
14586      */
14587     invalidHandleIds: null,
14588
14589     /**
14590      * An indexted array of css class names for elements that will be ignored
14591      * if clicked.
14592      * @property invalidHandleClasses
14593      * @type string[]
14594      */
14595     invalidHandleClasses: null,
14596
14597     /**
14598      * The linked element's absolute X position at the time the drag was
14599      * started
14600      * @property startPageX
14601      * @type int
14602      * @private
14603      */
14604     startPageX: 0,
14605
14606     /**
14607      * The linked element's absolute X position at the time the drag was
14608      * started
14609      * @property startPageY
14610      * @type int
14611      * @private
14612      */
14613     startPageY: 0,
14614
14615     /**
14616      * The group defines a logical collection of DragDrop objects that are
14617      * related.  Instances only get events when interacting with other
14618      * DragDrop object in the same group.  This lets us define multiple
14619      * groups using a single DragDrop subclass if we want.
14620      * @property groups
14621      * @type {string: string}
14622      */
14623     groups: null,
14624
14625     /**
14626      * Individual drag/drop instances can be locked.  This will prevent
14627      * onmousedown start drag.
14628      * @property locked
14629      * @type boolean
14630      * @private
14631      */
14632     locked: false,
14633
14634     /**
14635      * Lock this instance
14636      * @method lock
14637      */
14638     lock: function() { this.locked = true; },
14639
14640     /**
14641      * Unlock this instace
14642      * @method unlock
14643      */
14644     unlock: function() { this.locked = false; },
14645
14646     /**
14647      * By default, all insances can be a drop target.  This can be disabled by
14648      * setting isTarget to false.
14649      * @method isTarget
14650      * @type boolean
14651      */
14652     isTarget: true,
14653
14654     /**
14655      * The padding configured for this drag and drop object for calculating
14656      * the drop zone intersection with this object.
14657      * @method padding
14658      * @type int[]
14659      */
14660     padding: null,
14661
14662     /**
14663      * Cached reference to the linked element
14664      * @property _domRef
14665      * @private
14666      */
14667     _domRef: null,
14668
14669     /**
14670      * Internal typeof flag
14671      * @property __ygDragDrop
14672      * @private
14673      */
14674     __ygDragDrop: true,
14675
14676     /**
14677      * Set to true when horizontal contraints are applied
14678      * @property constrainX
14679      * @type boolean
14680      * @private
14681      */
14682     constrainX: false,
14683
14684     /**
14685      * Set to true when vertical contraints are applied
14686      * @property constrainY
14687      * @type boolean
14688      * @private
14689      */
14690     constrainY: false,
14691
14692     /**
14693      * The left constraint
14694      * @property minX
14695      * @type int
14696      * @private
14697      */
14698     minX: 0,
14699
14700     /**
14701      * The right constraint
14702      * @property maxX
14703      * @type int
14704      * @private
14705      */
14706     maxX: 0,
14707
14708     /**
14709      * The up constraint
14710      * @property minY
14711      * @type int
14712      * @type int
14713      * @private
14714      */
14715     minY: 0,
14716
14717     /**
14718      * The down constraint
14719      * @property maxY
14720      * @type int
14721      * @private
14722      */
14723     maxY: 0,
14724
14725     /**
14726      * Maintain offsets when we resetconstraints.  Set to true when you want
14727      * the position of the element relative to its parent to stay the same
14728      * when the page changes
14729      *
14730      * @property maintainOffset
14731      * @type boolean
14732      */
14733     maintainOffset: false,
14734
14735     /**
14736      * Array of pixel locations the element will snap to if we specified a
14737      * horizontal graduation/interval.  This array is generated automatically
14738      * when you define a tick interval.
14739      * @property xTicks
14740      * @type int[]
14741      */
14742     xTicks: null,
14743
14744     /**
14745      * Array of pixel locations the element will snap to if we specified a
14746      * vertical graduation/interval.  This array is generated automatically
14747      * when you define a tick interval.
14748      * @property yTicks
14749      * @type int[]
14750      */
14751     yTicks: null,
14752
14753     /**
14754      * By default the drag and drop instance will only respond to the primary
14755      * button click (left button for a right-handed mouse).  Set to true to
14756      * allow drag and drop to start with any mouse click that is propogated
14757      * by the browser
14758      * @property primaryButtonOnly
14759      * @type boolean
14760      */
14761     primaryButtonOnly: true,
14762
14763     /**
14764      * The availabe property is false until the linked dom element is accessible.
14765      * @property available
14766      * @type boolean
14767      */
14768     available: false,
14769
14770     /**
14771      * By default, drags can only be initiated if the mousedown occurs in the
14772      * region the linked element is.  This is done in part to work around a
14773      * bug in some browsers that mis-report the mousedown if the previous
14774      * mouseup happened outside of the window.  This property is set to true
14775      * if outer handles are defined.
14776      *
14777      * @property hasOuterHandles
14778      * @type boolean
14779      * @default false
14780      */
14781     hasOuterHandles: false,
14782
14783     /**
14784      * Code that executes immediately before the startDrag event
14785      * @method b4StartDrag
14786      * @private
14787      */
14788     b4StartDrag: function(x, y) { },
14789
14790     /**
14791      * Abstract method called after a drag/drop object is clicked
14792      * and the drag or mousedown time thresholds have beeen met.
14793      * @method startDrag
14794      * @param {int} X click location
14795      * @param {int} Y click location
14796      */
14797     startDrag: function(x, y) { /* override this */ },
14798
14799     /**
14800      * Code that executes immediately before the onDrag event
14801      * @method b4Drag
14802      * @private
14803      */
14804     b4Drag: function(e) { },
14805
14806     /**
14807      * Abstract method called during the onMouseMove event while dragging an
14808      * object.
14809      * @method onDrag
14810      * @param {Event} e the mousemove event
14811      */
14812     onDrag: function(e) { /* override this */ },
14813
14814     /**
14815      * Abstract method called when this element fist begins hovering over
14816      * another DragDrop obj
14817      * @method onDragEnter
14818      * @param {Event} e the mousemove event
14819      * @param {String|DragDrop[]} id In POINT mode, the element
14820      * id this is hovering over.  In INTERSECT mode, an array of one or more
14821      * dragdrop items being hovered over.
14822      */
14823     onDragEnter: function(e, id) { /* override this */ },
14824
14825     /**
14826      * Code that executes immediately before the onDragOver event
14827      * @method b4DragOver
14828      * @private
14829      */
14830     b4DragOver: function(e) { },
14831
14832     /**
14833      * Abstract method called when this element is hovering over another
14834      * DragDrop obj
14835      * @method onDragOver
14836      * @param {Event} e the mousemove event
14837      * @param {String|DragDrop[]} id In POINT mode, the element
14838      * id this is hovering over.  In INTERSECT mode, an array of dd items
14839      * being hovered over.
14840      */
14841     onDragOver: function(e, id) { /* override this */ },
14842
14843     /**
14844      * Code that executes immediately before the onDragOut event
14845      * @method b4DragOut
14846      * @private
14847      */
14848     b4DragOut: function(e) { },
14849
14850     /**
14851      * Abstract method called when we are no longer hovering over an element
14852      * @method onDragOut
14853      * @param {Event} e the mousemove event
14854      * @param {String|DragDrop[]} id In POINT mode, the element
14855      * id this was hovering over.  In INTERSECT mode, an array of dd items
14856      * that the mouse is no longer over.
14857      */
14858     onDragOut: function(e, id) { /* override this */ },
14859
14860     /**
14861      * Code that executes immediately before the onDragDrop event
14862      * @method b4DragDrop
14863      * @private
14864      */
14865     b4DragDrop: function(e) { },
14866
14867     /**
14868      * Abstract method called when this item is dropped on another DragDrop
14869      * obj
14870      * @method onDragDrop
14871      * @param {Event} e the mouseup event
14872      * @param {String|DragDrop[]} id In POINT mode, the element
14873      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14874      * was dropped on.
14875      */
14876     onDragDrop: function(e, id) { /* override this */ },
14877
14878     /**
14879      * Abstract method called when this item is dropped on an area with no
14880      * drop target
14881      * @method onInvalidDrop
14882      * @param {Event} e the mouseup event
14883      */
14884     onInvalidDrop: function(e) { /* override this */ },
14885
14886     /**
14887      * Code that executes immediately before the endDrag event
14888      * @method b4EndDrag
14889      * @private
14890      */
14891     b4EndDrag: function(e) { },
14892
14893     /**
14894      * Fired when we are done dragging the object
14895      * @method endDrag
14896      * @param {Event} e the mouseup event
14897      */
14898     endDrag: function(e) { /* override this */ },
14899
14900     /**
14901      * Code executed immediately before the onMouseDown event
14902      * @method b4MouseDown
14903      * @param {Event} e the mousedown event
14904      * @private
14905      */
14906     b4MouseDown: function(e) {  },
14907
14908     /**
14909      * Event handler that fires when a drag/drop obj gets a mousedown
14910      * @method onMouseDown
14911      * @param {Event} e the mousedown event
14912      */
14913     onMouseDown: function(e) { /* override this */ },
14914
14915     /**
14916      * Event handler that fires when a drag/drop obj gets a mouseup
14917      * @method onMouseUp
14918      * @param {Event} e the mouseup event
14919      */
14920     onMouseUp: function(e) { /* override this */ },
14921
14922     /**
14923      * Override the onAvailable method to do what is needed after the initial
14924      * position was determined.
14925      * @method onAvailable
14926      */
14927     onAvailable: function () {
14928     },
14929
14930     /*
14931      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14932      * @type Object
14933      */
14934     defaultPadding : {left:0, right:0, top:0, bottom:0},
14935
14936     /*
14937      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14938  *
14939  * Usage:
14940  <pre><code>
14941  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14942                 { dragElId: "existingProxyDiv" });
14943  dd.startDrag = function(){
14944      this.constrainTo("parent-id");
14945  };
14946  </code></pre>
14947  * Or you can initalize it using the {@link Roo.Element} object:
14948  <pre><code>
14949  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14950      startDrag : function(){
14951          this.constrainTo("parent-id");
14952      }
14953  });
14954  </code></pre>
14955      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14956      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14957      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14958      * an object containing the sides to pad. For example: {right:10, bottom:10}
14959      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14960      */
14961     constrainTo : function(constrainTo, pad, inContent){
14962         if(typeof pad == "number"){
14963             pad = {left: pad, right:pad, top:pad, bottom:pad};
14964         }
14965         pad = pad || this.defaultPadding;
14966         var b = Roo.get(this.getEl()).getBox();
14967         var ce = Roo.get(constrainTo);
14968         var s = ce.getScroll();
14969         var c, cd = ce.dom;
14970         if(cd == document.body){
14971             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14972         }else{
14973             xy = ce.getXY();
14974             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14975         }
14976
14977
14978         var topSpace = b.y - c.y;
14979         var leftSpace = b.x - c.x;
14980
14981         this.resetConstraints();
14982         this.setXConstraint(leftSpace - (pad.left||0), // left
14983                 c.width - leftSpace - b.width - (pad.right||0) //right
14984         );
14985         this.setYConstraint(topSpace - (pad.top||0), //top
14986                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14987         );
14988     },
14989
14990     /**
14991      * Returns a reference to the linked element
14992      * @method getEl
14993      * @return {HTMLElement} the html element
14994      */
14995     getEl: function() {
14996         if (!this._domRef) {
14997             this._domRef = Roo.getDom(this.id);
14998         }
14999
15000         return this._domRef;
15001     },
15002
15003     /**
15004      * Returns a reference to the actual element to drag.  By default this is
15005      * the same as the html element, but it can be assigned to another
15006      * element. An example of this can be found in Roo.dd.DDProxy
15007      * @method getDragEl
15008      * @return {HTMLElement} the html element
15009      */
15010     getDragEl: function() {
15011         return Roo.getDom(this.dragElId);
15012     },
15013
15014     /**
15015      * Sets up the DragDrop object.  Must be called in the constructor of any
15016      * Roo.dd.DragDrop subclass
15017      * @method init
15018      * @param id the id of the linked element
15019      * @param {String} sGroup the group of related items
15020      * @param {object} config configuration attributes
15021      */
15022     init: function(id, sGroup, config) {
15023         this.initTarget(id, sGroup, config);
15024         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15025         // Event.on(this.id, "selectstart", Event.preventDefault);
15026     },
15027
15028     /**
15029      * Initializes Targeting functionality only... the object does not
15030      * get a mousedown handler.
15031      * @method initTarget
15032      * @param id the id of the linked element
15033      * @param {String} sGroup the group of related items
15034      * @param {object} config configuration attributes
15035      */
15036     initTarget: function(id, sGroup, config) {
15037
15038         // configuration attributes
15039         this.config = config || {};
15040
15041         // create a local reference to the drag and drop manager
15042         this.DDM = Roo.dd.DDM;
15043         // initialize the groups array
15044         this.groups = {};
15045
15046         // assume that we have an element reference instead of an id if the
15047         // parameter is not a string
15048         if (typeof id !== "string") {
15049             id = Roo.id(id);
15050         }
15051
15052         // set the id
15053         this.id = id;
15054
15055         // add to an interaction group
15056         this.addToGroup((sGroup) ? sGroup : "default");
15057
15058         // We don't want to register this as the handle with the manager
15059         // so we just set the id rather than calling the setter.
15060         this.handleElId = id;
15061
15062         // the linked element is the element that gets dragged by default
15063         this.setDragElId(id);
15064
15065         // by default, clicked anchors will not start drag operations.
15066         this.invalidHandleTypes = { A: "A" };
15067         this.invalidHandleIds = {};
15068         this.invalidHandleClasses = [];
15069
15070         this.applyConfig();
15071
15072         this.handleOnAvailable();
15073     },
15074
15075     /**
15076      * Applies the configuration parameters that were passed into the constructor.
15077      * This is supposed to happen at each level through the inheritance chain.  So
15078      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15079      * DragDrop in order to get all of the parameters that are available in
15080      * each object.
15081      * @method applyConfig
15082      */
15083     applyConfig: function() {
15084
15085         // configurable properties:
15086         //    padding, isTarget, maintainOffset, primaryButtonOnly
15087         this.padding           = this.config.padding || [0, 0, 0, 0];
15088         this.isTarget          = (this.config.isTarget !== false);
15089         this.maintainOffset    = (this.config.maintainOffset);
15090         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15091
15092     },
15093
15094     /**
15095      * Executed when the linked element is available
15096      * @method handleOnAvailable
15097      * @private
15098      */
15099     handleOnAvailable: function() {
15100         this.available = true;
15101         this.resetConstraints();
15102         this.onAvailable();
15103     },
15104
15105      /**
15106      * Configures the padding for the target zone in px.  Effectively expands
15107      * (or reduces) the virtual object size for targeting calculations.
15108      * Supports css-style shorthand; if only one parameter is passed, all sides
15109      * will have that padding, and if only two are passed, the top and bottom
15110      * will have the first param, the left and right the second.
15111      * @method setPadding
15112      * @param {int} iTop    Top pad
15113      * @param {int} iRight  Right pad
15114      * @param {int} iBot    Bot pad
15115      * @param {int} iLeft   Left pad
15116      */
15117     setPadding: function(iTop, iRight, iBot, iLeft) {
15118         // this.padding = [iLeft, iRight, iTop, iBot];
15119         if (!iRight && 0 !== iRight) {
15120             this.padding = [iTop, iTop, iTop, iTop];
15121         } else if (!iBot && 0 !== iBot) {
15122             this.padding = [iTop, iRight, iTop, iRight];
15123         } else {
15124             this.padding = [iTop, iRight, iBot, iLeft];
15125         }
15126     },
15127
15128     /**
15129      * Stores the initial placement of the linked element.
15130      * @method setInitialPosition
15131      * @param {int} diffX   the X offset, default 0
15132      * @param {int} diffY   the Y offset, default 0
15133      */
15134     setInitPosition: function(diffX, diffY) {
15135         var el = this.getEl();
15136
15137         if (!this.DDM.verifyEl(el)) {
15138             return;
15139         }
15140
15141         var dx = diffX || 0;
15142         var dy = diffY || 0;
15143
15144         var p = Dom.getXY( el );
15145
15146         this.initPageX = p[0] - dx;
15147         this.initPageY = p[1] - dy;
15148
15149         this.lastPageX = p[0];
15150         this.lastPageY = p[1];
15151
15152
15153         this.setStartPosition(p);
15154     },
15155
15156     /**
15157      * Sets the start position of the element.  This is set when the obj
15158      * is initialized, the reset when a drag is started.
15159      * @method setStartPosition
15160      * @param pos current position (from previous lookup)
15161      * @private
15162      */
15163     setStartPosition: function(pos) {
15164         var p = pos || Dom.getXY( this.getEl() );
15165         this.deltaSetXY = null;
15166
15167         this.startPageX = p[0];
15168         this.startPageY = p[1];
15169     },
15170
15171     /**
15172      * Add this instance to a group of related drag/drop objects.  All
15173      * instances belong to at least one group, and can belong to as many
15174      * groups as needed.
15175      * @method addToGroup
15176      * @param sGroup {string} the name of the group
15177      */
15178     addToGroup: function(sGroup) {
15179         this.groups[sGroup] = true;
15180         this.DDM.regDragDrop(this, sGroup);
15181     },
15182
15183     /**
15184      * Remove's this instance from the supplied interaction group
15185      * @method removeFromGroup
15186      * @param {string}  sGroup  The group to drop
15187      */
15188     removeFromGroup: function(sGroup) {
15189         if (this.groups[sGroup]) {
15190             delete this.groups[sGroup];
15191         }
15192
15193         this.DDM.removeDDFromGroup(this, sGroup);
15194     },
15195
15196     /**
15197      * Allows you to specify that an element other than the linked element
15198      * will be moved with the cursor during a drag
15199      * @method setDragElId
15200      * @param id {string} the id of the element that will be used to initiate the drag
15201      */
15202     setDragElId: function(id) {
15203         this.dragElId = id;
15204     },
15205
15206     /**
15207      * Allows you to specify a child of the linked element that should be
15208      * used to initiate the drag operation.  An example of this would be if
15209      * you have a content div with text and links.  Clicking anywhere in the
15210      * content area would normally start the drag operation.  Use this method
15211      * to specify that an element inside of the content div is the element
15212      * that starts the drag operation.
15213      * @method setHandleElId
15214      * @param id {string} the id of the element that will be used to
15215      * initiate the drag.
15216      */
15217     setHandleElId: function(id) {
15218         if (typeof id !== "string") {
15219             id = Roo.id(id);
15220         }
15221         this.handleElId = id;
15222         this.DDM.regHandle(this.id, id);
15223     },
15224
15225     /**
15226      * Allows you to set an element outside of the linked element as a drag
15227      * handle
15228      * @method setOuterHandleElId
15229      * @param id the id of the element that will be used to initiate the drag
15230      */
15231     setOuterHandleElId: function(id) {
15232         if (typeof id !== "string") {
15233             id = Roo.id(id);
15234         }
15235         Event.on(id, "mousedown",
15236                 this.handleMouseDown, this);
15237         this.setHandleElId(id);
15238
15239         this.hasOuterHandles = true;
15240     },
15241
15242     /**
15243      * Remove all drag and drop hooks for this element
15244      * @method unreg
15245      */
15246     unreg: function() {
15247         Event.un(this.id, "mousedown",
15248                 this.handleMouseDown);
15249         this._domRef = null;
15250         this.DDM._remove(this);
15251     },
15252
15253     destroy : function(){
15254         this.unreg();
15255     },
15256
15257     /**
15258      * Returns true if this instance is locked, or the drag drop mgr is locked
15259      * (meaning that all drag/drop is disabled on the page.)
15260      * @method isLocked
15261      * @return {boolean} true if this obj or all drag/drop is locked, else
15262      * false
15263      */
15264     isLocked: function() {
15265         return (this.DDM.isLocked() || this.locked);
15266     },
15267
15268     /**
15269      * Fired when this object is clicked
15270      * @method handleMouseDown
15271      * @param {Event} e
15272      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15273      * @private
15274      */
15275     handleMouseDown: function(e, oDD){
15276         if (this.primaryButtonOnly && e.button != 0) {
15277             return;
15278         }
15279
15280         if (this.isLocked()) {
15281             return;
15282         }
15283
15284         this.DDM.refreshCache(this.groups);
15285
15286         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15287         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15288         } else {
15289             if (this.clickValidator(e)) {
15290
15291                 // set the initial element position
15292                 this.setStartPosition();
15293
15294
15295                 this.b4MouseDown(e);
15296                 this.onMouseDown(e);
15297
15298                 this.DDM.handleMouseDown(e, this);
15299
15300                 this.DDM.stopEvent(e);
15301             } else {
15302
15303
15304             }
15305         }
15306     },
15307
15308     clickValidator: function(e) {
15309         var target = e.getTarget();
15310         return ( this.isValidHandleChild(target) &&
15311                     (this.id == this.handleElId ||
15312                         this.DDM.handleWasClicked(target, this.id)) );
15313     },
15314
15315     /**
15316      * Allows you to specify a tag name that should not start a drag operation
15317      * when clicked.  This is designed to facilitate embedding links within a
15318      * drag handle that do something other than start the drag.
15319      * @method addInvalidHandleType
15320      * @param {string} tagName the type of element to exclude
15321      */
15322     addInvalidHandleType: function(tagName) {
15323         var type = tagName.toUpperCase();
15324         this.invalidHandleTypes[type] = type;
15325     },
15326
15327     /**
15328      * Lets you to specify an element id for a child of a drag handle
15329      * that should not initiate a drag
15330      * @method addInvalidHandleId
15331      * @param {string} id the element id of the element you wish to ignore
15332      */
15333     addInvalidHandleId: function(id) {
15334         if (typeof id !== "string") {
15335             id = Roo.id(id);
15336         }
15337         this.invalidHandleIds[id] = id;
15338     },
15339
15340     /**
15341      * Lets you specify a css class of elements that will not initiate a drag
15342      * @method addInvalidHandleClass
15343      * @param {string} cssClass the class of the elements you wish to ignore
15344      */
15345     addInvalidHandleClass: function(cssClass) {
15346         this.invalidHandleClasses.push(cssClass);
15347     },
15348
15349     /**
15350      * Unsets an excluded tag name set by addInvalidHandleType
15351      * @method removeInvalidHandleType
15352      * @param {string} tagName the type of element to unexclude
15353      */
15354     removeInvalidHandleType: function(tagName) {
15355         var type = tagName.toUpperCase();
15356         // this.invalidHandleTypes[type] = null;
15357         delete this.invalidHandleTypes[type];
15358     },
15359
15360     /**
15361      * Unsets an invalid handle id
15362      * @method removeInvalidHandleId
15363      * @param {string} id the id of the element to re-enable
15364      */
15365     removeInvalidHandleId: function(id) {
15366         if (typeof id !== "string") {
15367             id = Roo.id(id);
15368         }
15369         delete this.invalidHandleIds[id];
15370     },
15371
15372     /**
15373      * Unsets an invalid css class
15374      * @method removeInvalidHandleClass
15375      * @param {string} cssClass the class of the element(s) you wish to
15376      * re-enable
15377      */
15378     removeInvalidHandleClass: function(cssClass) {
15379         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15380             if (this.invalidHandleClasses[i] == cssClass) {
15381                 delete this.invalidHandleClasses[i];
15382             }
15383         }
15384     },
15385
15386     /**
15387      * Checks the tag exclusion list to see if this click should be ignored
15388      * @method isValidHandleChild
15389      * @param {HTMLElement} node the HTMLElement to evaluate
15390      * @return {boolean} true if this is a valid tag type, false if not
15391      */
15392     isValidHandleChild: function(node) {
15393
15394         var valid = true;
15395         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15396         var nodeName;
15397         try {
15398             nodeName = node.nodeName.toUpperCase();
15399         } catch(e) {
15400             nodeName = node.nodeName;
15401         }
15402         valid = valid && !this.invalidHandleTypes[nodeName];
15403         valid = valid && !this.invalidHandleIds[node.id];
15404
15405         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15406             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15407         }
15408
15409
15410         return valid;
15411
15412     },
15413
15414     /**
15415      * Create the array of horizontal tick marks if an interval was specified
15416      * in setXConstraint().
15417      * @method setXTicks
15418      * @private
15419      */
15420     setXTicks: function(iStartX, iTickSize) {
15421         this.xTicks = [];
15422         this.xTickSize = iTickSize;
15423
15424         var tickMap = {};
15425
15426         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15427             if (!tickMap[i]) {
15428                 this.xTicks[this.xTicks.length] = i;
15429                 tickMap[i] = true;
15430             }
15431         }
15432
15433         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15434             if (!tickMap[i]) {
15435                 this.xTicks[this.xTicks.length] = i;
15436                 tickMap[i] = true;
15437             }
15438         }
15439
15440         this.xTicks.sort(this.DDM.numericSort) ;
15441     },
15442
15443     /**
15444      * Create the array of vertical tick marks if an interval was specified in
15445      * setYConstraint().
15446      * @method setYTicks
15447      * @private
15448      */
15449     setYTicks: function(iStartY, iTickSize) {
15450         this.yTicks = [];
15451         this.yTickSize = iTickSize;
15452
15453         var tickMap = {};
15454
15455         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15456             if (!tickMap[i]) {
15457                 this.yTicks[this.yTicks.length] = i;
15458                 tickMap[i] = true;
15459             }
15460         }
15461
15462         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15463             if (!tickMap[i]) {
15464                 this.yTicks[this.yTicks.length] = i;
15465                 tickMap[i] = true;
15466             }
15467         }
15468
15469         this.yTicks.sort(this.DDM.numericSort) ;
15470     },
15471
15472     /**
15473      * By default, the element can be dragged any place on the screen.  Use
15474      * this method to limit the horizontal travel of the element.  Pass in
15475      * 0,0 for the parameters if you want to lock the drag to the y axis.
15476      * @method setXConstraint
15477      * @param {int} iLeft the number of pixels the element can move to the left
15478      * @param {int} iRight the number of pixels the element can move to the
15479      * right
15480      * @param {int} iTickSize optional parameter for specifying that the
15481      * element
15482      * should move iTickSize pixels at a time.
15483      */
15484     setXConstraint: function(iLeft, iRight, iTickSize) {
15485         this.leftConstraint = iLeft;
15486         this.rightConstraint = iRight;
15487
15488         this.minX = this.initPageX - iLeft;
15489         this.maxX = this.initPageX + iRight;
15490         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15491
15492         this.constrainX = true;
15493     },
15494
15495     /**
15496      * Clears any constraints applied to this instance.  Also clears ticks
15497      * since they can't exist independent of a constraint at this time.
15498      * @method clearConstraints
15499      */
15500     clearConstraints: function() {
15501         this.constrainX = false;
15502         this.constrainY = false;
15503         this.clearTicks();
15504     },
15505
15506     /**
15507      * Clears any tick interval defined for this instance
15508      * @method clearTicks
15509      */
15510     clearTicks: function() {
15511         this.xTicks = null;
15512         this.yTicks = null;
15513         this.xTickSize = 0;
15514         this.yTickSize = 0;
15515     },
15516
15517     /**
15518      * By default, the element can be dragged any place on the screen.  Set
15519      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15520      * parameters if you want to lock the drag to the x axis.
15521      * @method setYConstraint
15522      * @param {int} iUp the number of pixels the element can move up
15523      * @param {int} iDown the number of pixels the element can move down
15524      * @param {int} iTickSize optional parameter for specifying that the
15525      * element should move iTickSize pixels at a time.
15526      */
15527     setYConstraint: function(iUp, iDown, iTickSize) {
15528         this.topConstraint = iUp;
15529         this.bottomConstraint = iDown;
15530
15531         this.minY = this.initPageY - iUp;
15532         this.maxY = this.initPageY + iDown;
15533         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15534
15535         this.constrainY = true;
15536
15537     },
15538
15539     /**
15540      * resetConstraints must be called if you manually reposition a dd element.
15541      * @method resetConstraints
15542      * @param {boolean} maintainOffset
15543      */
15544     resetConstraints: function() {
15545
15546
15547         // Maintain offsets if necessary
15548         if (this.initPageX || this.initPageX === 0) {
15549             // figure out how much this thing has moved
15550             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15551             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15552
15553             this.setInitPosition(dx, dy);
15554
15555         // This is the first time we have detected the element's position
15556         } else {
15557             this.setInitPosition();
15558         }
15559
15560         if (this.constrainX) {
15561             this.setXConstraint( this.leftConstraint,
15562                                  this.rightConstraint,
15563                                  this.xTickSize        );
15564         }
15565
15566         if (this.constrainY) {
15567             this.setYConstraint( this.topConstraint,
15568                                  this.bottomConstraint,
15569                                  this.yTickSize         );
15570         }
15571     },
15572
15573     /**
15574      * Normally the drag element is moved pixel by pixel, but we can specify
15575      * that it move a number of pixels at a time.  This method resolves the
15576      * location when we have it set up like this.
15577      * @method getTick
15578      * @param {int} val where we want to place the object
15579      * @param {int[]} tickArray sorted array of valid points
15580      * @return {int} the closest tick
15581      * @private
15582      */
15583     getTick: function(val, tickArray) {
15584
15585         if (!tickArray) {
15586             // If tick interval is not defined, it is effectively 1 pixel,
15587             // so we return the value passed to us.
15588             return val;
15589         } else if (tickArray[0] >= val) {
15590             // The value is lower than the first tick, so we return the first
15591             // tick.
15592             return tickArray[0];
15593         } else {
15594             for (var i=0, len=tickArray.length; i<len; ++i) {
15595                 var next = i + 1;
15596                 if (tickArray[next] && tickArray[next] >= val) {
15597                     var diff1 = val - tickArray[i];
15598                     var diff2 = tickArray[next] - val;
15599                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15600                 }
15601             }
15602
15603             // The value is larger than the last tick, so we return the last
15604             // tick.
15605             return tickArray[tickArray.length - 1];
15606         }
15607     },
15608
15609     /**
15610      * toString method
15611      * @method toString
15612      * @return {string} string representation of the dd obj
15613      */
15614     toString: function() {
15615         return ("DragDrop " + this.id);
15616     }
15617
15618 });
15619
15620 })();
15621 /*
15622  * Based on:
15623  * Ext JS Library 1.1.1
15624  * Copyright(c) 2006-2007, Ext JS, LLC.
15625  *
15626  * Originally Released Under LGPL - original licence link has changed is not relivant.
15627  *
15628  * Fork - LGPL
15629  * <script type="text/javascript">
15630  */
15631
15632
15633 /**
15634  * The drag and drop utility provides a framework for building drag and drop
15635  * applications.  In addition to enabling drag and drop for specific elements,
15636  * the drag and drop elements are tracked by the manager class, and the
15637  * interactions between the various elements are tracked during the drag and
15638  * the implementing code is notified about these important moments.
15639  */
15640
15641 // Only load the library once.  Rewriting the manager class would orphan
15642 // existing drag and drop instances.
15643 if (!Roo.dd.DragDropMgr) {
15644
15645 /**
15646  * @class Roo.dd.DragDropMgr
15647  * DragDropMgr is a singleton that tracks the element interaction for
15648  * all DragDrop items in the window.  Generally, you will not call
15649  * this class directly, but it does have helper methods that could
15650  * be useful in your DragDrop implementations.
15651  * @singleton
15652  */
15653 Roo.dd.DragDropMgr = function() {
15654
15655     var Event = Roo.EventManager;
15656
15657     return {
15658
15659         /**
15660          * Two dimensional Array of registered DragDrop objects.  The first
15661          * dimension is the DragDrop item group, the second the DragDrop
15662          * object.
15663          * @property ids
15664          * @type {string: string}
15665          * @private
15666          * @static
15667          */
15668         ids: {},
15669
15670         /**
15671          * Array of element ids defined as drag handles.  Used to determine
15672          * if the element that generated the mousedown event is actually the
15673          * handle and not the html element itself.
15674          * @property handleIds
15675          * @type {string: string}
15676          * @private
15677          * @static
15678          */
15679         handleIds: {},
15680
15681         /**
15682          * the DragDrop object that is currently being dragged
15683          * @property dragCurrent
15684          * @type DragDrop
15685          * @private
15686          * @static
15687          **/
15688         dragCurrent: null,
15689
15690         /**
15691          * the DragDrop object(s) that are being hovered over
15692          * @property dragOvers
15693          * @type Array
15694          * @private
15695          * @static
15696          */
15697         dragOvers: {},
15698
15699         /**
15700          * the X distance between the cursor and the object being dragged
15701          * @property deltaX
15702          * @type int
15703          * @private
15704          * @static
15705          */
15706         deltaX: 0,
15707
15708         /**
15709          * the Y distance between the cursor and the object being dragged
15710          * @property deltaY
15711          * @type int
15712          * @private
15713          * @static
15714          */
15715         deltaY: 0,
15716
15717         /**
15718          * Flag to determine if we should prevent the default behavior of the
15719          * events we define. By default this is true, but this can be set to
15720          * false if you need the default behavior (not recommended)
15721          * @property preventDefault
15722          * @type boolean
15723          * @static
15724          */
15725         preventDefault: true,
15726
15727         /**
15728          * Flag to determine if we should stop the propagation of the events
15729          * we generate. This is true by default but you may want to set it to
15730          * false if the html element contains other features that require the
15731          * mouse click.
15732          * @property stopPropagation
15733          * @type boolean
15734          * @static
15735          */
15736         stopPropagation: true,
15737
15738         /**
15739          * Internal flag that is set to true when drag and drop has been
15740          * intialized
15741          * @property initialized
15742          * @private
15743          * @static
15744          */
15745         initalized: false,
15746
15747         /**
15748          * All drag and drop can be disabled.
15749          * @property locked
15750          * @private
15751          * @static
15752          */
15753         locked: false,
15754
15755         /**
15756          * Called the first time an element is registered.
15757          * @method init
15758          * @private
15759          * @static
15760          */
15761         init: function() {
15762             this.initialized = true;
15763         },
15764
15765         /**
15766          * In point mode, drag and drop interaction is defined by the
15767          * location of the cursor during the drag/drop
15768          * @property POINT
15769          * @type int
15770          * @static
15771          */
15772         POINT: 0,
15773
15774         /**
15775          * In intersect mode, drag and drop interactio nis defined by the
15776          * overlap of two or more drag and drop objects.
15777          * @property INTERSECT
15778          * @type int
15779          * @static
15780          */
15781         INTERSECT: 1,
15782
15783         /**
15784          * The current drag and drop mode.  Default: POINT
15785          * @property mode
15786          * @type int
15787          * @static
15788          */
15789         mode: 0,
15790
15791         /**
15792          * Runs method on all drag and drop objects
15793          * @method _execOnAll
15794          * @private
15795          * @static
15796          */
15797         _execOnAll: function(sMethod, args) {
15798             for (var i in this.ids) {
15799                 for (var j in this.ids[i]) {
15800                     var oDD = this.ids[i][j];
15801                     if (! this.isTypeOfDD(oDD)) {
15802                         continue;
15803                     }
15804                     oDD[sMethod].apply(oDD, args);
15805                 }
15806             }
15807         },
15808
15809         /**
15810          * Drag and drop initialization.  Sets up the global event handlers
15811          * @method _onLoad
15812          * @private
15813          * @static
15814          */
15815         _onLoad: function() {
15816
15817             this.init();
15818
15819
15820             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15821             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15822             Event.on(window,   "unload",    this._onUnload, this, true);
15823             Event.on(window,   "resize",    this._onResize, this, true);
15824             // Event.on(window,   "mouseout",    this._test);
15825
15826         },
15827
15828         /**
15829          * Reset constraints on all drag and drop objs
15830          * @method _onResize
15831          * @private
15832          * @static
15833          */
15834         _onResize: function(e) {
15835             this._execOnAll("resetConstraints", []);
15836         },
15837
15838         /**
15839          * Lock all drag and drop functionality
15840          * @method lock
15841          * @static
15842          */
15843         lock: function() { this.locked = true; },
15844
15845         /**
15846          * Unlock all drag and drop functionality
15847          * @method unlock
15848          * @static
15849          */
15850         unlock: function() { this.locked = false; },
15851
15852         /**
15853          * Is drag and drop locked?
15854          * @method isLocked
15855          * @return {boolean} True if drag and drop is locked, false otherwise.
15856          * @static
15857          */
15858         isLocked: function() { return this.locked; },
15859
15860         /**
15861          * Location cache that is set for all drag drop objects when a drag is
15862          * initiated, cleared when the drag is finished.
15863          * @property locationCache
15864          * @private
15865          * @static
15866          */
15867         locationCache: {},
15868
15869         /**
15870          * Set useCache to false if you want to force object the lookup of each
15871          * drag and drop linked element constantly during a drag.
15872          * @property useCache
15873          * @type boolean
15874          * @static
15875          */
15876         useCache: true,
15877
15878         /**
15879          * The number of pixels that the mouse needs to move after the
15880          * mousedown before the drag is initiated.  Default=3;
15881          * @property clickPixelThresh
15882          * @type int
15883          * @static
15884          */
15885         clickPixelThresh: 3,
15886
15887         /**
15888          * The number of milliseconds after the mousedown event to initiate the
15889          * drag if we don't get a mouseup event. Default=1000
15890          * @property clickTimeThresh
15891          * @type int
15892          * @static
15893          */
15894         clickTimeThresh: 350,
15895
15896         /**
15897          * Flag that indicates that either the drag pixel threshold or the
15898          * mousdown time threshold has been met
15899          * @property dragThreshMet
15900          * @type boolean
15901          * @private
15902          * @static
15903          */
15904         dragThreshMet: false,
15905
15906         /**
15907          * Timeout used for the click time threshold
15908          * @property clickTimeout
15909          * @type Object
15910          * @private
15911          * @static
15912          */
15913         clickTimeout: null,
15914
15915         /**
15916          * The X position of the mousedown event stored for later use when a
15917          * drag threshold is met.
15918          * @property startX
15919          * @type int
15920          * @private
15921          * @static
15922          */
15923         startX: 0,
15924
15925         /**
15926          * The Y position of the mousedown event stored for later use when a
15927          * drag threshold is met.
15928          * @property startY
15929          * @type int
15930          * @private
15931          * @static
15932          */
15933         startY: 0,
15934
15935         /**
15936          * Each DragDrop instance must be registered with the DragDropMgr.
15937          * This is executed in DragDrop.init()
15938          * @method regDragDrop
15939          * @param {DragDrop} oDD the DragDrop object to register
15940          * @param {String} sGroup the name of the group this element belongs to
15941          * @static
15942          */
15943         regDragDrop: function(oDD, sGroup) {
15944             if (!this.initialized) { this.init(); }
15945
15946             if (!this.ids[sGroup]) {
15947                 this.ids[sGroup] = {};
15948             }
15949             this.ids[sGroup][oDD.id] = oDD;
15950         },
15951
15952         /**
15953          * Removes the supplied dd instance from the supplied group. Executed
15954          * by DragDrop.removeFromGroup, so don't call this function directly.
15955          * @method removeDDFromGroup
15956          * @private
15957          * @static
15958          */
15959         removeDDFromGroup: function(oDD, sGroup) {
15960             if (!this.ids[sGroup]) {
15961                 this.ids[sGroup] = {};
15962             }
15963
15964             var obj = this.ids[sGroup];
15965             if (obj && obj[oDD.id]) {
15966                 delete obj[oDD.id];
15967             }
15968         },
15969
15970         /**
15971          * Unregisters a drag and drop item.  This is executed in
15972          * DragDrop.unreg, use that method instead of calling this directly.
15973          * @method _remove
15974          * @private
15975          * @static
15976          */
15977         _remove: function(oDD) {
15978             for (var g in oDD.groups) {
15979                 if (g && this.ids[g][oDD.id]) {
15980                     delete this.ids[g][oDD.id];
15981                 }
15982             }
15983             delete this.handleIds[oDD.id];
15984         },
15985
15986         /**
15987          * Each DragDrop handle element must be registered.  This is done
15988          * automatically when executing DragDrop.setHandleElId()
15989          * @method regHandle
15990          * @param {String} sDDId the DragDrop id this element is a handle for
15991          * @param {String} sHandleId the id of the element that is the drag
15992          * handle
15993          * @static
15994          */
15995         regHandle: function(sDDId, sHandleId) {
15996             if (!this.handleIds[sDDId]) {
15997                 this.handleIds[sDDId] = {};
15998             }
15999             this.handleIds[sDDId][sHandleId] = sHandleId;
16000         },
16001
16002         /**
16003          * Utility function to determine if a given element has been
16004          * registered as a drag drop item.
16005          * @method isDragDrop
16006          * @param {String} id the element id to check
16007          * @return {boolean} true if this element is a DragDrop item,
16008          * false otherwise
16009          * @static
16010          */
16011         isDragDrop: function(id) {
16012             return ( this.getDDById(id) ) ? true : false;
16013         },
16014
16015         /**
16016          * Returns the drag and drop instances that are in all groups the
16017          * passed in instance belongs to.
16018          * @method getRelated
16019          * @param {DragDrop} p_oDD the obj to get related data for
16020          * @param {boolean} bTargetsOnly if true, only return targetable objs
16021          * @return {DragDrop[]} the related instances
16022          * @static
16023          */
16024         getRelated: function(p_oDD, bTargetsOnly) {
16025             var oDDs = [];
16026             for (var i in p_oDD.groups) {
16027                 for (j in this.ids[i]) {
16028                     var dd = this.ids[i][j];
16029                     if (! this.isTypeOfDD(dd)) {
16030                         continue;
16031                     }
16032                     if (!bTargetsOnly || dd.isTarget) {
16033                         oDDs[oDDs.length] = dd;
16034                     }
16035                 }
16036             }
16037
16038             return oDDs;
16039         },
16040
16041         /**
16042          * Returns true if the specified dd target is a legal target for
16043          * the specifice drag obj
16044          * @method isLegalTarget
16045          * @param {DragDrop} the drag obj
16046          * @param {DragDrop} the target
16047          * @return {boolean} true if the target is a legal target for the
16048          * dd obj
16049          * @static
16050          */
16051         isLegalTarget: function (oDD, oTargetDD) {
16052             var targets = this.getRelated(oDD, true);
16053             for (var i=0, len=targets.length;i<len;++i) {
16054                 if (targets[i].id == oTargetDD.id) {
16055                     return true;
16056                 }
16057             }
16058
16059             return false;
16060         },
16061
16062         /**
16063          * My goal is to be able to transparently determine if an object is
16064          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16065          * returns "object", oDD.constructor.toString() always returns
16066          * "DragDrop" and not the name of the subclass.  So for now it just
16067          * evaluates a well-known variable in DragDrop.
16068          * @method isTypeOfDD
16069          * @param {Object} the object to evaluate
16070          * @return {boolean} true if typeof oDD = DragDrop
16071          * @static
16072          */
16073         isTypeOfDD: function (oDD) {
16074             return (oDD && oDD.__ygDragDrop);
16075         },
16076
16077         /**
16078          * Utility function to determine if a given element has been
16079          * registered as a drag drop handle for the given Drag Drop object.
16080          * @method isHandle
16081          * @param {String} id the element id to check
16082          * @return {boolean} true if this element is a DragDrop handle, false
16083          * otherwise
16084          * @static
16085          */
16086         isHandle: function(sDDId, sHandleId) {
16087             return ( this.handleIds[sDDId] &&
16088                             this.handleIds[sDDId][sHandleId] );
16089         },
16090
16091         /**
16092          * Returns the DragDrop instance for a given id
16093          * @method getDDById
16094          * @param {String} id the id of the DragDrop object
16095          * @return {DragDrop} the drag drop object, null if it is not found
16096          * @static
16097          */
16098         getDDById: function(id) {
16099             for (var i in this.ids) {
16100                 if (this.ids[i][id]) {
16101                     return this.ids[i][id];
16102                 }
16103             }
16104             return null;
16105         },
16106
16107         /**
16108          * Fired after a registered DragDrop object gets the mousedown event.
16109          * Sets up the events required to track the object being dragged
16110          * @method handleMouseDown
16111          * @param {Event} e the event
16112          * @param oDD the DragDrop object being dragged
16113          * @private
16114          * @static
16115          */
16116         handleMouseDown: function(e, oDD) {
16117             if(Roo.QuickTips){
16118                 Roo.QuickTips.disable();
16119             }
16120             this.currentTarget = e.getTarget();
16121
16122             this.dragCurrent = oDD;
16123
16124             var el = oDD.getEl();
16125
16126             // track start position
16127             this.startX = e.getPageX();
16128             this.startY = e.getPageY();
16129
16130             this.deltaX = this.startX - el.offsetLeft;
16131             this.deltaY = this.startY - el.offsetTop;
16132
16133             this.dragThreshMet = false;
16134
16135             this.clickTimeout = setTimeout(
16136                     function() {
16137                         var DDM = Roo.dd.DDM;
16138                         DDM.startDrag(DDM.startX, DDM.startY);
16139                     },
16140                     this.clickTimeThresh );
16141         },
16142
16143         /**
16144          * Fired when either the drag pixel threshol or the mousedown hold
16145          * time threshold has been met.
16146          * @method startDrag
16147          * @param x {int} the X position of the original mousedown
16148          * @param y {int} the Y position of the original mousedown
16149          * @static
16150          */
16151         startDrag: function(x, y) {
16152             clearTimeout(this.clickTimeout);
16153             if (this.dragCurrent) {
16154                 this.dragCurrent.b4StartDrag(x, y);
16155                 this.dragCurrent.startDrag(x, y);
16156             }
16157             this.dragThreshMet = true;
16158         },
16159
16160         /**
16161          * Internal function to handle the mouseup event.  Will be invoked
16162          * from the context of the document.
16163          * @method handleMouseUp
16164          * @param {Event} e the event
16165          * @private
16166          * @static
16167          */
16168         handleMouseUp: function(e) {
16169
16170             if(Roo.QuickTips){
16171                 Roo.QuickTips.enable();
16172             }
16173             if (! this.dragCurrent) {
16174                 return;
16175             }
16176
16177             clearTimeout(this.clickTimeout);
16178
16179             if (this.dragThreshMet) {
16180                 this.fireEvents(e, true);
16181             } else {
16182             }
16183
16184             this.stopDrag(e);
16185
16186             this.stopEvent(e);
16187         },
16188
16189         /**
16190          * Utility to stop event propagation and event default, if these
16191          * features are turned on.
16192          * @method stopEvent
16193          * @param {Event} e the event as returned by this.getEvent()
16194          * @static
16195          */
16196         stopEvent: function(e){
16197             if(this.stopPropagation) {
16198                 e.stopPropagation();
16199             }
16200
16201             if (this.preventDefault) {
16202                 e.preventDefault();
16203             }
16204         },
16205
16206         /**
16207          * Internal function to clean up event handlers after the drag
16208          * operation is complete
16209          * @method stopDrag
16210          * @param {Event} e the event
16211          * @private
16212          * @static
16213          */
16214         stopDrag: function(e) {
16215             // Fire the drag end event for the item that was dragged
16216             if (this.dragCurrent) {
16217                 if (this.dragThreshMet) {
16218                     this.dragCurrent.b4EndDrag(e);
16219                     this.dragCurrent.endDrag(e);
16220                 }
16221
16222                 this.dragCurrent.onMouseUp(e);
16223             }
16224
16225             this.dragCurrent = null;
16226             this.dragOvers = {};
16227         },
16228
16229         /**
16230          * Internal function to handle the mousemove event.  Will be invoked
16231          * from the context of the html element.
16232          *
16233          * @TODO figure out what we can do about mouse events lost when the
16234          * user drags objects beyond the window boundary.  Currently we can
16235          * detect this in internet explorer by verifying that the mouse is
16236          * down during the mousemove event.  Firefox doesn't give us the
16237          * button state on the mousemove event.
16238          * @method handleMouseMove
16239          * @param {Event} e the event
16240          * @private
16241          * @static
16242          */
16243         handleMouseMove: function(e) {
16244             if (! this.dragCurrent) {
16245                 return true;
16246             }
16247
16248             // var button = e.which || e.button;
16249
16250             // check for IE mouseup outside of page boundary
16251             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16252                 this.stopEvent(e);
16253                 return this.handleMouseUp(e);
16254             }
16255
16256             if (!this.dragThreshMet) {
16257                 var diffX = Math.abs(this.startX - e.getPageX());
16258                 var diffY = Math.abs(this.startY - e.getPageY());
16259                 if (diffX > this.clickPixelThresh ||
16260                             diffY > this.clickPixelThresh) {
16261                     this.startDrag(this.startX, this.startY);
16262                 }
16263             }
16264
16265             if (this.dragThreshMet) {
16266                 this.dragCurrent.b4Drag(e);
16267                 this.dragCurrent.onDrag(e);
16268                 if(!this.dragCurrent.moveOnly){
16269                     this.fireEvents(e, false);
16270                 }
16271             }
16272
16273             this.stopEvent(e);
16274
16275             return true;
16276         },
16277
16278         /**
16279          * Iterates over all of the DragDrop elements to find ones we are
16280          * hovering over or dropping on
16281          * @method fireEvents
16282          * @param {Event} e the event
16283          * @param {boolean} isDrop is this a drop op or a mouseover op?
16284          * @private
16285          * @static
16286          */
16287         fireEvents: function(e, isDrop) {
16288             var dc = this.dragCurrent;
16289
16290             // If the user did the mouse up outside of the window, we could
16291             // get here even though we have ended the drag.
16292             if (!dc || dc.isLocked()) {
16293                 return;
16294             }
16295
16296             var pt = e.getPoint();
16297
16298             // cache the previous dragOver array
16299             var oldOvers = [];
16300
16301             var outEvts   = [];
16302             var overEvts  = [];
16303             var dropEvts  = [];
16304             var enterEvts = [];
16305
16306             // Check to see if the object(s) we were hovering over is no longer
16307             // being hovered over so we can fire the onDragOut event
16308             for (var i in this.dragOvers) {
16309
16310                 var ddo = this.dragOvers[i];
16311
16312                 if (! this.isTypeOfDD(ddo)) {
16313                     continue;
16314                 }
16315
16316                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16317                     outEvts.push( ddo );
16318                 }
16319
16320                 oldOvers[i] = true;
16321                 delete this.dragOvers[i];
16322             }
16323
16324             for (var sGroup in dc.groups) {
16325
16326                 if ("string" != typeof sGroup) {
16327                     continue;
16328                 }
16329
16330                 for (i in this.ids[sGroup]) {
16331                     var oDD = this.ids[sGroup][i];
16332                     if (! this.isTypeOfDD(oDD)) {
16333                         continue;
16334                     }
16335
16336                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16337                         if (this.isOverTarget(pt, oDD, this.mode)) {
16338                             // look for drop interactions
16339                             if (isDrop) {
16340                                 dropEvts.push( oDD );
16341                             // look for drag enter and drag over interactions
16342                             } else {
16343
16344                                 // initial drag over: dragEnter fires
16345                                 if (!oldOvers[oDD.id]) {
16346                                     enterEvts.push( oDD );
16347                                 // subsequent drag overs: dragOver fires
16348                                 } else {
16349                                     overEvts.push( oDD );
16350                                 }
16351
16352                                 this.dragOvers[oDD.id] = oDD;
16353                             }
16354                         }
16355                     }
16356                 }
16357             }
16358
16359             if (this.mode) {
16360                 if (outEvts.length) {
16361                     dc.b4DragOut(e, outEvts);
16362                     dc.onDragOut(e, outEvts);
16363                 }
16364
16365                 if (enterEvts.length) {
16366                     dc.onDragEnter(e, enterEvts);
16367                 }
16368
16369                 if (overEvts.length) {
16370                     dc.b4DragOver(e, overEvts);
16371                     dc.onDragOver(e, overEvts);
16372                 }
16373
16374                 if (dropEvts.length) {
16375                     dc.b4DragDrop(e, dropEvts);
16376                     dc.onDragDrop(e, dropEvts);
16377                 }
16378
16379             } else {
16380                 // fire dragout events
16381                 var len = 0;
16382                 for (i=0, len=outEvts.length; i<len; ++i) {
16383                     dc.b4DragOut(e, outEvts[i].id);
16384                     dc.onDragOut(e, outEvts[i].id);
16385                 }
16386
16387                 // fire enter events
16388                 for (i=0,len=enterEvts.length; i<len; ++i) {
16389                     // dc.b4DragEnter(e, oDD.id);
16390                     dc.onDragEnter(e, enterEvts[i].id);
16391                 }
16392
16393                 // fire over events
16394                 for (i=0,len=overEvts.length; i<len; ++i) {
16395                     dc.b4DragOver(e, overEvts[i].id);
16396                     dc.onDragOver(e, overEvts[i].id);
16397                 }
16398
16399                 // fire drop events
16400                 for (i=0, len=dropEvts.length; i<len; ++i) {
16401                     dc.b4DragDrop(e, dropEvts[i].id);
16402                     dc.onDragDrop(e, dropEvts[i].id);
16403                 }
16404
16405             }
16406
16407             // notify about a drop that did not find a target
16408             if (isDrop && !dropEvts.length) {
16409                 dc.onInvalidDrop(e);
16410             }
16411
16412         },
16413
16414         /**
16415          * Helper function for getting the best match from the list of drag
16416          * and drop objects returned by the drag and drop events when we are
16417          * in INTERSECT mode.  It returns either the first object that the
16418          * cursor is over, or the object that has the greatest overlap with
16419          * the dragged element.
16420          * @method getBestMatch
16421          * @param  {DragDrop[]} dds The array of drag and drop objects
16422          * targeted
16423          * @return {DragDrop}       The best single match
16424          * @static
16425          */
16426         getBestMatch: function(dds) {
16427             var winner = null;
16428             // Return null if the input is not what we expect
16429             //if (!dds || !dds.length || dds.length == 0) {
16430                // winner = null;
16431             // If there is only one item, it wins
16432             //} else if (dds.length == 1) {
16433
16434             var len = dds.length;
16435
16436             if (len == 1) {
16437                 winner = dds[0];
16438             } else {
16439                 // Loop through the targeted items
16440                 for (var i=0; i<len; ++i) {
16441                     var dd = dds[i];
16442                     // If the cursor is over the object, it wins.  If the
16443                     // cursor is over multiple matches, the first one we come
16444                     // to wins.
16445                     if (dd.cursorIsOver) {
16446                         winner = dd;
16447                         break;
16448                     // Otherwise the object with the most overlap wins
16449                     } else {
16450                         if (!winner ||
16451                             winner.overlap.getArea() < dd.overlap.getArea()) {
16452                             winner = dd;
16453                         }
16454                     }
16455                 }
16456             }
16457
16458             return winner;
16459         },
16460
16461         /**
16462          * Refreshes the cache of the top-left and bottom-right points of the
16463          * drag and drop objects in the specified group(s).  This is in the
16464          * format that is stored in the drag and drop instance, so typical
16465          * usage is:
16466          * <code>
16467          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16468          * </code>
16469          * Alternatively:
16470          * <code>
16471          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16472          * </code>
16473          * @TODO this really should be an indexed array.  Alternatively this
16474          * method could accept both.
16475          * @method refreshCache
16476          * @param {Object} groups an associative array of groups to refresh
16477          * @static
16478          */
16479         refreshCache: function(groups) {
16480             for (var sGroup in groups) {
16481                 if ("string" != typeof sGroup) {
16482                     continue;
16483                 }
16484                 for (var i in this.ids[sGroup]) {
16485                     var oDD = this.ids[sGroup][i];
16486
16487                     if (this.isTypeOfDD(oDD)) {
16488                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16489                         var loc = this.getLocation(oDD);
16490                         if (loc) {
16491                             this.locationCache[oDD.id] = loc;
16492                         } else {
16493                             delete this.locationCache[oDD.id];
16494                             // this will unregister the drag and drop object if
16495                             // the element is not in a usable state
16496                             // oDD.unreg();
16497                         }
16498                     }
16499                 }
16500             }
16501         },
16502
16503         /**
16504          * This checks to make sure an element exists and is in the DOM.  The
16505          * main purpose is to handle cases where innerHTML is used to remove
16506          * drag and drop objects from the DOM.  IE provides an 'unspecified
16507          * error' when trying to access the offsetParent of such an element
16508          * @method verifyEl
16509          * @param {HTMLElement} el the element to check
16510          * @return {boolean} true if the element looks usable
16511          * @static
16512          */
16513         verifyEl: function(el) {
16514             if (el) {
16515                 var parent;
16516                 if(Roo.isIE){
16517                     try{
16518                         parent = el.offsetParent;
16519                     }catch(e){}
16520                 }else{
16521                     parent = el.offsetParent;
16522                 }
16523                 if (parent) {
16524                     return true;
16525                 }
16526             }
16527
16528             return false;
16529         },
16530
16531         /**
16532          * Returns a Region object containing the drag and drop element's position
16533          * and size, including the padding configured for it
16534          * @method getLocation
16535          * @param {DragDrop} oDD the drag and drop object to get the
16536          *                       location for
16537          * @return {Roo.lib.Region} a Region object representing the total area
16538          *                             the element occupies, including any padding
16539          *                             the instance is configured for.
16540          * @static
16541          */
16542         getLocation: function(oDD) {
16543             if (! this.isTypeOfDD(oDD)) {
16544                 return null;
16545             }
16546
16547             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16548
16549             try {
16550                 pos= Roo.lib.Dom.getXY(el);
16551             } catch (e) { }
16552
16553             if (!pos) {
16554                 return null;
16555             }
16556
16557             x1 = pos[0];
16558             x2 = x1 + el.offsetWidth;
16559             y1 = pos[1];
16560             y2 = y1 + el.offsetHeight;
16561
16562             t = y1 - oDD.padding[0];
16563             r = x2 + oDD.padding[1];
16564             b = y2 + oDD.padding[2];
16565             l = x1 - oDD.padding[3];
16566
16567             return new Roo.lib.Region( t, r, b, l );
16568         },
16569
16570         /**
16571          * Checks the cursor location to see if it over the target
16572          * @method isOverTarget
16573          * @param {Roo.lib.Point} pt The point to evaluate
16574          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16575          * @return {boolean} true if the mouse is over the target
16576          * @private
16577          * @static
16578          */
16579         isOverTarget: function(pt, oTarget, intersect) {
16580             // use cache if available
16581             var loc = this.locationCache[oTarget.id];
16582             if (!loc || !this.useCache) {
16583                 loc = this.getLocation(oTarget);
16584                 this.locationCache[oTarget.id] = loc;
16585
16586             }
16587
16588             if (!loc) {
16589                 return false;
16590             }
16591
16592             oTarget.cursorIsOver = loc.contains( pt );
16593
16594             // DragDrop is using this as a sanity check for the initial mousedown
16595             // in this case we are done.  In POINT mode, if the drag obj has no
16596             // contraints, we are also done. Otherwise we need to evaluate the
16597             // location of the target as related to the actual location of the
16598             // dragged element.
16599             var dc = this.dragCurrent;
16600             if (!dc || !dc.getTargetCoord ||
16601                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16602                 return oTarget.cursorIsOver;
16603             }
16604
16605             oTarget.overlap = null;
16606
16607             // Get the current location of the drag element, this is the
16608             // location of the mouse event less the delta that represents
16609             // where the original mousedown happened on the element.  We
16610             // need to consider constraints and ticks as well.
16611             var pos = dc.getTargetCoord(pt.x, pt.y);
16612
16613             var el = dc.getDragEl();
16614             var curRegion = new Roo.lib.Region( pos.y,
16615                                                    pos.x + el.offsetWidth,
16616                                                    pos.y + el.offsetHeight,
16617                                                    pos.x );
16618
16619             var overlap = curRegion.intersect(loc);
16620
16621             if (overlap) {
16622                 oTarget.overlap = overlap;
16623                 return (intersect) ? true : oTarget.cursorIsOver;
16624             } else {
16625                 return false;
16626             }
16627         },
16628
16629         /**
16630          * unload event handler
16631          * @method _onUnload
16632          * @private
16633          * @static
16634          */
16635         _onUnload: function(e, me) {
16636             Roo.dd.DragDropMgr.unregAll();
16637         },
16638
16639         /**
16640          * Cleans up the drag and drop events and objects.
16641          * @method unregAll
16642          * @private
16643          * @static
16644          */
16645         unregAll: function() {
16646
16647             if (this.dragCurrent) {
16648                 this.stopDrag();
16649                 this.dragCurrent = null;
16650             }
16651
16652             this._execOnAll("unreg", []);
16653
16654             for (i in this.elementCache) {
16655                 delete this.elementCache[i];
16656             }
16657
16658             this.elementCache = {};
16659             this.ids = {};
16660         },
16661
16662         /**
16663          * A cache of DOM elements
16664          * @property elementCache
16665          * @private
16666          * @static
16667          */
16668         elementCache: {},
16669
16670         /**
16671          * Get the wrapper for the DOM element specified
16672          * @method getElWrapper
16673          * @param {String} id the id of the element to get
16674          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16675          * @private
16676          * @deprecated This wrapper isn't that useful
16677          * @static
16678          */
16679         getElWrapper: function(id) {
16680             var oWrapper = this.elementCache[id];
16681             if (!oWrapper || !oWrapper.el) {
16682                 oWrapper = this.elementCache[id] =
16683                     new this.ElementWrapper(Roo.getDom(id));
16684             }
16685             return oWrapper;
16686         },
16687
16688         /**
16689          * Returns the actual DOM element
16690          * @method getElement
16691          * @param {String} id the id of the elment to get
16692          * @return {Object} The element
16693          * @deprecated use Roo.getDom instead
16694          * @static
16695          */
16696         getElement: function(id) {
16697             return Roo.getDom(id);
16698         },
16699
16700         /**
16701          * Returns the style property for the DOM element (i.e.,
16702          * document.getElById(id).style)
16703          * @method getCss
16704          * @param {String} id the id of the elment to get
16705          * @return {Object} The style property of the element
16706          * @deprecated use Roo.getDom instead
16707          * @static
16708          */
16709         getCss: function(id) {
16710             var el = Roo.getDom(id);
16711             return (el) ? el.style : null;
16712         },
16713
16714         /**
16715          * Inner class for cached elements
16716          * @class DragDropMgr.ElementWrapper
16717          * @for DragDropMgr
16718          * @private
16719          * @deprecated
16720          */
16721         ElementWrapper: function(el) {
16722                 /**
16723                  * The element
16724                  * @property el
16725                  */
16726                 this.el = el || null;
16727                 /**
16728                  * The element id
16729                  * @property id
16730                  */
16731                 this.id = this.el && el.id;
16732                 /**
16733                  * A reference to the style property
16734                  * @property css
16735                  */
16736                 this.css = this.el && el.style;
16737             },
16738
16739         /**
16740          * Returns the X position of an html element
16741          * @method getPosX
16742          * @param el the element for which to get the position
16743          * @return {int} the X coordinate
16744          * @for DragDropMgr
16745          * @deprecated use Roo.lib.Dom.getX instead
16746          * @static
16747          */
16748         getPosX: function(el) {
16749             return Roo.lib.Dom.getX(el);
16750         },
16751
16752         /**
16753          * Returns the Y position of an html element
16754          * @method getPosY
16755          * @param el the element for which to get the position
16756          * @return {int} the Y coordinate
16757          * @deprecated use Roo.lib.Dom.getY instead
16758          * @static
16759          */
16760         getPosY: function(el) {
16761             return Roo.lib.Dom.getY(el);
16762         },
16763
16764         /**
16765          * Swap two nodes.  In IE, we use the native method, for others we
16766          * emulate the IE behavior
16767          * @method swapNode
16768          * @param n1 the first node to swap
16769          * @param n2 the other node to swap
16770          * @static
16771          */
16772         swapNode: function(n1, n2) {
16773             if (n1.swapNode) {
16774                 n1.swapNode(n2);
16775             } else {
16776                 var p = n2.parentNode;
16777                 var s = n2.nextSibling;
16778
16779                 if (s == n1) {
16780                     p.insertBefore(n1, n2);
16781                 } else if (n2 == n1.nextSibling) {
16782                     p.insertBefore(n2, n1);
16783                 } else {
16784                     n1.parentNode.replaceChild(n2, n1);
16785                     p.insertBefore(n1, s);
16786                 }
16787             }
16788         },
16789
16790         /**
16791          * Returns the current scroll position
16792          * @method getScroll
16793          * @private
16794          * @static
16795          */
16796         getScroll: function () {
16797             var t, l, dde=document.documentElement, db=document.body;
16798             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16799                 t = dde.scrollTop;
16800                 l = dde.scrollLeft;
16801             } else if (db) {
16802                 t = db.scrollTop;
16803                 l = db.scrollLeft;
16804             } else {
16805
16806             }
16807             return { top: t, left: l };
16808         },
16809
16810         /**
16811          * Returns the specified element style property
16812          * @method getStyle
16813          * @param {HTMLElement} el          the element
16814          * @param {string}      styleProp   the style property
16815          * @return {string} The value of the style property
16816          * @deprecated use Roo.lib.Dom.getStyle
16817          * @static
16818          */
16819         getStyle: function(el, styleProp) {
16820             return Roo.fly(el).getStyle(styleProp);
16821         },
16822
16823         /**
16824          * Gets the scrollTop
16825          * @method getScrollTop
16826          * @return {int} the document's scrollTop
16827          * @static
16828          */
16829         getScrollTop: function () { return this.getScroll().top; },
16830
16831         /**
16832          * Gets the scrollLeft
16833          * @method getScrollLeft
16834          * @return {int} the document's scrollTop
16835          * @static
16836          */
16837         getScrollLeft: function () { return this.getScroll().left; },
16838
16839         /**
16840          * Sets the x/y position of an element to the location of the
16841          * target element.
16842          * @method moveToEl
16843          * @param {HTMLElement} moveEl      The element to move
16844          * @param {HTMLElement} targetEl    The position reference element
16845          * @static
16846          */
16847         moveToEl: function (moveEl, targetEl) {
16848             var aCoord = Roo.lib.Dom.getXY(targetEl);
16849             Roo.lib.Dom.setXY(moveEl, aCoord);
16850         },
16851
16852         /**
16853          * Numeric array sort function
16854          * @method numericSort
16855          * @static
16856          */
16857         numericSort: function(a, b) { return (a - b); },
16858
16859         /**
16860          * Internal counter
16861          * @property _timeoutCount
16862          * @private
16863          * @static
16864          */
16865         _timeoutCount: 0,
16866
16867         /**
16868          * Trying to make the load order less important.  Without this we get
16869          * an error if this file is loaded before the Event Utility.
16870          * @method _addListeners
16871          * @private
16872          * @static
16873          */
16874         _addListeners: function() {
16875             var DDM = Roo.dd.DDM;
16876             if ( Roo.lib.Event && document ) {
16877                 DDM._onLoad();
16878             } else {
16879                 if (DDM._timeoutCount > 2000) {
16880                 } else {
16881                     setTimeout(DDM._addListeners, 10);
16882                     if (document && document.body) {
16883                         DDM._timeoutCount += 1;
16884                     }
16885                 }
16886             }
16887         },
16888
16889         /**
16890          * Recursively searches the immediate parent and all child nodes for
16891          * the handle element in order to determine wheter or not it was
16892          * clicked.
16893          * @method handleWasClicked
16894          * @param node the html element to inspect
16895          * @static
16896          */
16897         handleWasClicked: function(node, id) {
16898             if (this.isHandle(id, node.id)) {
16899                 return true;
16900             } else {
16901                 // check to see if this is a text node child of the one we want
16902                 var p = node.parentNode;
16903
16904                 while (p) {
16905                     if (this.isHandle(id, p.id)) {
16906                         return true;
16907                     } else {
16908                         p = p.parentNode;
16909                     }
16910                 }
16911             }
16912
16913             return false;
16914         }
16915
16916     };
16917
16918 }();
16919
16920 // shorter alias, save a few bytes
16921 Roo.dd.DDM = Roo.dd.DragDropMgr;
16922 Roo.dd.DDM._addListeners();
16923
16924 }/*
16925  * Based on:
16926  * Ext JS Library 1.1.1
16927  * Copyright(c) 2006-2007, Ext JS, LLC.
16928  *
16929  * Originally Released Under LGPL - original licence link has changed is not relivant.
16930  *
16931  * Fork - LGPL
16932  * <script type="text/javascript">
16933  */
16934
16935 /**
16936  * @class Roo.dd.DD
16937  * A DragDrop implementation where the linked element follows the
16938  * mouse cursor during a drag.
16939  * @extends Roo.dd.DragDrop
16940  * @constructor
16941  * @param {String} id the id of the linked element
16942  * @param {String} sGroup the group of related DragDrop items
16943  * @param {object} config an object containing configurable attributes
16944  *                Valid properties for DD:
16945  *                    scroll
16946  */
16947 Roo.dd.DD = function(id, sGroup, config) {
16948     if (id) {
16949         this.init(id, sGroup, config);
16950     }
16951 };
16952
16953 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16954
16955     /**
16956      * When set to true, the utility automatically tries to scroll the browser
16957      * window wehn a drag and drop element is dragged near the viewport boundary.
16958      * Defaults to true.
16959      * @property scroll
16960      * @type boolean
16961      */
16962     scroll: true,
16963
16964     /**
16965      * Sets the pointer offset to the distance between the linked element's top
16966      * left corner and the location the element was clicked
16967      * @method autoOffset
16968      * @param {int} iPageX the X coordinate of the click
16969      * @param {int} iPageY the Y coordinate of the click
16970      */
16971     autoOffset: function(iPageX, iPageY) {
16972         var x = iPageX - this.startPageX;
16973         var y = iPageY - this.startPageY;
16974         this.setDelta(x, y);
16975     },
16976
16977     /**
16978      * Sets the pointer offset.  You can call this directly to force the
16979      * offset to be in a particular location (e.g., pass in 0,0 to set it
16980      * to the center of the object)
16981      * @method setDelta
16982      * @param {int} iDeltaX the distance from the left
16983      * @param {int} iDeltaY the distance from the top
16984      */
16985     setDelta: function(iDeltaX, iDeltaY) {
16986         this.deltaX = iDeltaX;
16987         this.deltaY = iDeltaY;
16988     },
16989
16990     /**
16991      * Sets the drag element to the location of the mousedown or click event,
16992      * maintaining the cursor location relative to the location on the element
16993      * that was clicked.  Override this if you want to place the element in a
16994      * location other than where the cursor is.
16995      * @method setDragElPos
16996      * @param {int} iPageX the X coordinate of the mousedown or drag event
16997      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16998      */
16999     setDragElPos: function(iPageX, iPageY) {
17000         // the first time we do this, we are going to check to make sure
17001         // the element has css positioning
17002
17003         var el = this.getDragEl();
17004         this.alignElWithMouse(el, iPageX, iPageY);
17005     },
17006
17007     /**
17008      * Sets the element to the location of the mousedown or click event,
17009      * maintaining the cursor location relative to the location on the element
17010      * that was clicked.  Override this if you want to place the element in a
17011      * location other than where the cursor is.
17012      * @method alignElWithMouse
17013      * @param {HTMLElement} el the element to move
17014      * @param {int} iPageX the X coordinate of the mousedown or drag event
17015      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17016      */
17017     alignElWithMouse: function(el, iPageX, iPageY) {
17018         var oCoord = this.getTargetCoord(iPageX, iPageY);
17019         var fly = el.dom ? el : Roo.fly(el);
17020         if (!this.deltaSetXY) {
17021             var aCoord = [oCoord.x, oCoord.y];
17022             fly.setXY(aCoord);
17023             var newLeft = fly.getLeft(true);
17024             var newTop  = fly.getTop(true);
17025             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17026         } else {
17027             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17028         }
17029
17030         this.cachePosition(oCoord.x, oCoord.y);
17031         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17032         return oCoord;
17033     },
17034
17035     /**
17036      * Saves the most recent position so that we can reset the constraints and
17037      * tick marks on-demand.  We need to know this so that we can calculate the
17038      * number of pixels the element is offset from its original position.
17039      * @method cachePosition
17040      * @param iPageX the current x position (optional, this just makes it so we
17041      * don't have to look it up again)
17042      * @param iPageY the current y position (optional, this just makes it so we
17043      * don't have to look it up again)
17044      */
17045     cachePosition: function(iPageX, iPageY) {
17046         if (iPageX) {
17047             this.lastPageX = iPageX;
17048             this.lastPageY = iPageY;
17049         } else {
17050             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17051             this.lastPageX = aCoord[0];
17052             this.lastPageY = aCoord[1];
17053         }
17054     },
17055
17056     /**
17057      * Auto-scroll the window if the dragged object has been moved beyond the
17058      * visible window boundary.
17059      * @method autoScroll
17060      * @param {int} x the drag element's x position
17061      * @param {int} y the drag element's y position
17062      * @param {int} h the height of the drag element
17063      * @param {int} w the width of the drag element
17064      * @private
17065      */
17066     autoScroll: function(x, y, h, w) {
17067
17068         if (this.scroll) {
17069             // The client height
17070             var clientH = Roo.lib.Dom.getViewWidth();
17071
17072             // The client width
17073             var clientW = Roo.lib.Dom.getViewHeight();
17074
17075             // The amt scrolled down
17076             var st = this.DDM.getScrollTop();
17077
17078             // The amt scrolled right
17079             var sl = this.DDM.getScrollLeft();
17080
17081             // Location of the bottom of the element
17082             var bot = h + y;
17083
17084             // Location of the right of the element
17085             var right = w + x;
17086
17087             // The distance from the cursor to the bottom of the visible area,
17088             // adjusted so that we don't scroll if the cursor is beyond the
17089             // element drag constraints
17090             var toBot = (clientH + st - y - this.deltaY);
17091
17092             // The distance from the cursor to the right of the visible area
17093             var toRight = (clientW + sl - x - this.deltaX);
17094
17095
17096             // How close to the edge the cursor must be before we scroll
17097             // var thresh = (document.all) ? 100 : 40;
17098             var thresh = 40;
17099
17100             // How many pixels to scroll per autoscroll op.  This helps to reduce
17101             // clunky scrolling. IE is more sensitive about this ... it needs this
17102             // value to be higher.
17103             var scrAmt = (document.all) ? 80 : 30;
17104
17105             // Scroll down if we are near the bottom of the visible page and the
17106             // obj extends below the crease
17107             if ( bot > clientH && toBot < thresh ) {
17108                 window.scrollTo(sl, st + scrAmt);
17109             }
17110
17111             // Scroll up if the window is scrolled down and the top of the object
17112             // goes above the top border
17113             if ( y < st && st > 0 && y - st < thresh ) {
17114                 window.scrollTo(sl, st - scrAmt);
17115             }
17116
17117             // Scroll right if the obj is beyond the right border and the cursor is
17118             // near the border.
17119             if ( right > clientW && toRight < thresh ) {
17120                 window.scrollTo(sl + scrAmt, st);
17121             }
17122
17123             // Scroll left if the window has been scrolled to the right and the obj
17124             // extends past the left border
17125             if ( x < sl && sl > 0 && x - sl < thresh ) {
17126                 window.scrollTo(sl - scrAmt, st);
17127             }
17128         }
17129     },
17130
17131     /**
17132      * Finds the location the element should be placed if we want to move
17133      * it to where the mouse location less the click offset would place us.
17134      * @method getTargetCoord
17135      * @param {int} iPageX the X coordinate of the click
17136      * @param {int} iPageY the Y coordinate of the click
17137      * @return an object that contains the coordinates (Object.x and Object.y)
17138      * @private
17139      */
17140     getTargetCoord: function(iPageX, iPageY) {
17141
17142
17143         var x = iPageX - this.deltaX;
17144         var y = iPageY - this.deltaY;
17145
17146         if (this.constrainX) {
17147             if (x < this.minX) { x = this.minX; }
17148             if (x > this.maxX) { x = this.maxX; }
17149         }
17150
17151         if (this.constrainY) {
17152             if (y < this.minY) { y = this.minY; }
17153             if (y > this.maxY) { y = this.maxY; }
17154         }
17155
17156         x = this.getTick(x, this.xTicks);
17157         y = this.getTick(y, this.yTicks);
17158
17159
17160         return {x:x, y:y};
17161     },
17162
17163     /*
17164      * Sets up config options specific to this class. Overrides
17165      * Roo.dd.DragDrop, but all versions of this method through the
17166      * inheritance chain are called
17167      */
17168     applyConfig: function() {
17169         Roo.dd.DD.superclass.applyConfig.call(this);
17170         this.scroll = (this.config.scroll !== false);
17171     },
17172
17173     /*
17174      * Event that fires prior to the onMouseDown event.  Overrides
17175      * Roo.dd.DragDrop.
17176      */
17177     b4MouseDown: function(e) {
17178         // this.resetConstraints();
17179         this.autoOffset(e.getPageX(),
17180                             e.getPageY());
17181     },
17182
17183     /*
17184      * Event that fires prior to the onDrag event.  Overrides
17185      * Roo.dd.DragDrop.
17186      */
17187     b4Drag: function(e) {
17188         this.setDragElPos(e.getPageX(),
17189                             e.getPageY());
17190     },
17191
17192     toString: function() {
17193         return ("DD " + this.id);
17194     }
17195
17196     //////////////////////////////////////////////////////////////////////////
17197     // Debugging ygDragDrop events that can be overridden
17198     //////////////////////////////////////////////////////////////////////////
17199     /*
17200     startDrag: function(x, y) {
17201     },
17202
17203     onDrag: function(e) {
17204     },
17205
17206     onDragEnter: function(e, id) {
17207     },
17208
17209     onDragOver: function(e, id) {
17210     },
17211
17212     onDragOut: function(e, id) {
17213     },
17214
17215     onDragDrop: function(e, id) {
17216     },
17217
17218     endDrag: function(e) {
17219     }
17220
17221     */
17222
17223 });/*
17224  * Based on:
17225  * Ext JS Library 1.1.1
17226  * Copyright(c) 2006-2007, Ext JS, LLC.
17227  *
17228  * Originally Released Under LGPL - original licence link has changed is not relivant.
17229  *
17230  * Fork - LGPL
17231  * <script type="text/javascript">
17232  */
17233
17234 /**
17235  * @class Roo.dd.DDProxy
17236  * A DragDrop implementation that inserts an empty, bordered div into
17237  * the document that follows the cursor during drag operations.  At the time of
17238  * the click, the frame div is resized to the dimensions of the linked html
17239  * element, and moved to the exact location of the linked element.
17240  *
17241  * References to the "frame" element refer to the single proxy element that
17242  * was created to be dragged in place of all DDProxy elements on the
17243  * page.
17244  *
17245  * @extends Roo.dd.DD
17246  * @constructor
17247  * @param {String} id the id of the linked html element
17248  * @param {String} sGroup the group of related DragDrop objects
17249  * @param {object} config an object containing configurable attributes
17250  *                Valid properties for DDProxy in addition to those in DragDrop:
17251  *                   resizeFrame, centerFrame, dragElId
17252  */
17253 Roo.dd.DDProxy = function(id, sGroup, config) {
17254     if (id) {
17255         this.init(id, sGroup, config);
17256         this.initFrame();
17257     }
17258 };
17259
17260 /**
17261  * The default drag frame div id
17262  * @property Roo.dd.DDProxy.dragElId
17263  * @type String
17264  * @static
17265  */
17266 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17267
17268 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17269
17270     /**
17271      * By default we resize the drag frame to be the same size as the element
17272      * we want to drag (this is to get the frame effect).  We can turn it off
17273      * if we want a different behavior.
17274      * @property resizeFrame
17275      * @type boolean
17276      */
17277     resizeFrame: true,
17278
17279     /**
17280      * By default the frame is positioned exactly where the drag element is, so
17281      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17282      * you do not have constraints on the obj is to have the drag frame centered
17283      * around the cursor.  Set centerFrame to true for this effect.
17284      * @property centerFrame
17285      * @type boolean
17286      */
17287     centerFrame: false,
17288
17289     /**
17290      * Creates the proxy element if it does not yet exist
17291      * @method createFrame
17292      */
17293     createFrame: function() {
17294         var self = this;
17295         var body = document.body;
17296
17297         if (!body || !body.firstChild) {
17298             setTimeout( function() { self.createFrame(); }, 50 );
17299             return;
17300         }
17301
17302         var div = this.getDragEl();
17303
17304         if (!div) {
17305             div    = document.createElement("div");
17306             div.id = this.dragElId;
17307             var s  = div.style;
17308
17309             s.position   = "absolute";
17310             s.visibility = "hidden";
17311             s.cursor     = "move";
17312             s.border     = "2px solid #aaa";
17313             s.zIndex     = 999;
17314
17315             // appendChild can blow up IE if invoked prior to the window load event
17316             // while rendering a table.  It is possible there are other scenarios
17317             // that would cause this to happen as well.
17318             body.insertBefore(div, body.firstChild);
17319         }
17320     },
17321
17322     /**
17323      * Initialization for the drag frame element.  Must be called in the
17324      * constructor of all subclasses
17325      * @method initFrame
17326      */
17327     initFrame: function() {
17328         this.createFrame();
17329     },
17330
17331     applyConfig: function() {
17332         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17333
17334         this.resizeFrame = (this.config.resizeFrame !== false);
17335         this.centerFrame = (this.config.centerFrame);
17336         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17337     },
17338
17339     /**
17340      * Resizes the drag frame to the dimensions of the clicked object, positions
17341      * it over the object, and finally displays it
17342      * @method showFrame
17343      * @param {int} iPageX X click position
17344      * @param {int} iPageY Y click position
17345      * @private
17346      */
17347     showFrame: function(iPageX, iPageY) {
17348         var el = this.getEl();
17349         var dragEl = this.getDragEl();
17350         var s = dragEl.style;
17351
17352         this._resizeProxy();
17353
17354         if (this.centerFrame) {
17355             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17356                            Math.round(parseInt(s.height, 10)/2) );
17357         }
17358
17359         this.setDragElPos(iPageX, iPageY);
17360
17361         Roo.fly(dragEl).show();
17362     },
17363
17364     /**
17365      * The proxy is automatically resized to the dimensions of the linked
17366      * element when a drag is initiated, unless resizeFrame is set to false
17367      * @method _resizeProxy
17368      * @private
17369      */
17370     _resizeProxy: function() {
17371         if (this.resizeFrame) {
17372             var el = this.getEl();
17373             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17374         }
17375     },
17376
17377     // overrides Roo.dd.DragDrop
17378     b4MouseDown: function(e) {
17379         var x = e.getPageX();
17380         var y = e.getPageY();
17381         this.autoOffset(x, y);
17382         this.setDragElPos(x, y);
17383     },
17384
17385     // overrides Roo.dd.DragDrop
17386     b4StartDrag: function(x, y) {
17387         // show the drag frame
17388         this.showFrame(x, y);
17389     },
17390
17391     // overrides Roo.dd.DragDrop
17392     b4EndDrag: function(e) {
17393         Roo.fly(this.getDragEl()).hide();
17394     },
17395
17396     // overrides Roo.dd.DragDrop
17397     // By default we try to move the element to the last location of the frame.
17398     // This is so that the default behavior mirrors that of Roo.dd.DD.
17399     endDrag: function(e) {
17400
17401         var lel = this.getEl();
17402         var del = this.getDragEl();
17403
17404         // Show the drag frame briefly so we can get its position
17405         del.style.visibility = "";
17406
17407         this.beforeMove();
17408         // Hide the linked element before the move to get around a Safari
17409         // rendering bug.
17410         lel.style.visibility = "hidden";
17411         Roo.dd.DDM.moveToEl(lel, del);
17412         del.style.visibility = "hidden";
17413         lel.style.visibility = "";
17414
17415         this.afterDrag();
17416     },
17417
17418     beforeMove : function(){
17419
17420     },
17421
17422     afterDrag : function(){
17423
17424     },
17425
17426     toString: function() {
17427         return ("DDProxy " + this.id);
17428     }
17429
17430 });
17431 /*
17432  * Based on:
17433  * Ext JS Library 1.1.1
17434  * Copyright(c) 2006-2007, Ext JS, LLC.
17435  *
17436  * Originally Released Under LGPL - original licence link has changed is not relivant.
17437  *
17438  * Fork - LGPL
17439  * <script type="text/javascript">
17440  */
17441
17442  /**
17443  * @class Roo.dd.DDTarget
17444  * A DragDrop implementation that does not move, but can be a drop
17445  * target.  You would get the same result by simply omitting implementation
17446  * for the event callbacks, but this way we reduce the processing cost of the
17447  * event listener and the callbacks.
17448  * @extends Roo.dd.DragDrop
17449  * @constructor
17450  * @param {String} id the id of the element that is a drop target
17451  * @param {String} sGroup the group of related DragDrop objects
17452  * @param {object} config an object containing configurable attributes
17453  *                 Valid properties for DDTarget in addition to those in
17454  *                 DragDrop:
17455  *                    none
17456  */
17457 Roo.dd.DDTarget = function(id, sGroup, config) {
17458     if (id) {
17459         this.initTarget(id, sGroup, config);
17460     }
17461     if (config.listeners || config.events) { 
17462        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17463             listeners : config.listeners || {}, 
17464             events : config.events || {} 
17465         });    
17466     }
17467 };
17468
17469 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17470 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17471     toString: function() {
17472         return ("DDTarget " + this.id);
17473     }
17474 });
17475 /*
17476  * Based on:
17477  * Ext JS Library 1.1.1
17478  * Copyright(c) 2006-2007, Ext JS, LLC.
17479  *
17480  * Originally Released Under LGPL - original licence link has changed is not relivant.
17481  *
17482  * Fork - LGPL
17483  * <script type="text/javascript">
17484  */
17485  
17486
17487 /**
17488  * @class Roo.dd.ScrollManager
17489  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17490  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17491  * @singleton
17492  */
17493 Roo.dd.ScrollManager = function(){
17494     var ddm = Roo.dd.DragDropMgr;
17495     var els = {};
17496     var dragEl = null;
17497     var proc = {};
17498     
17499     var onStop = function(e){
17500         dragEl = null;
17501         clearProc();
17502     };
17503     
17504     var triggerRefresh = function(){
17505         if(ddm.dragCurrent){
17506              ddm.refreshCache(ddm.dragCurrent.groups);
17507         }
17508     };
17509     
17510     var doScroll = function(){
17511         if(ddm.dragCurrent){
17512             var dds = Roo.dd.ScrollManager;
17513             if(!dds.animate){
17514                 if(proc.el.scroll(proc.dir, dds.increment)){
17515                     triggerRefresh();
17516                 }
17517             }else{
17518                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17519             }
17520         }
17521     };
17522     
17523     var clearProc = function(){
17524         if(proc.id){
17525             clearInterval(proc.id);
17526         }
17527         proc.id = 0;
17528         proc.el = null;
17529         proc.dir = "";
17530     };
17531     
17532     var startProc = function(el, dir){
17533         clearProc();
17534         proc.el = el;
17535         proc.dir = dir;
17536         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17537     };
17538     
17539     var onFire = function(e, isDrop){
17540         if(isDrop || !ddm.dragCurrent){ return; }
17541         var dds = Roo.dd.ScrollManager;
17542         if(!dragEl || dragEl != ddm.dragCurrent){
17543             dragEl = ddm.dragCurrent;
17544             // refresh regions on drag start
17545             dds.refreshCache();
17546         }
17547         
17548         var xy = Roo.lib.Event.getXY(e);
17549         var pt = new Roo.lib.Point(xy[0], xy[1]);
17550         for(var id in els){
17551             var el = els[id], r = el._region;
17552             if(r && r.contains(pt) && el.isScrollable()){
17553                 if(r.bottom - pt.y <= dds.thresh){
17554                     if(proc.el != el){
17555                         startProc(el, "down");
17556                     }
17557                     return;
17558                 }else if(r.right - pt.x <= dds.thresh){
17559                     if(proc.el != el){
17560                         startProc(el, "left");
17561                     }
17562                     return;
17563                 }else if(pt.y - r.top <= dds.thresh){
17564                     if(proc.el != el){
17565                         startProc(el, "up");
17566                     }
17567                     return;
17568                 }else if(pt.x - r.left <= dds.thresh){
17569                     if(proc.el != el){
17570                         startProc(el, "right");
17571                     }
17572                     return;
17573                 }
17574             }
17575         }
17576         clearProc();
17577     };
17578     
17579     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17580     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17581     
17582     return {
17583         /**
17584          * Registers new overflow element(s) to auto scroll
17585          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17586          */
17587         register : function(el){
17588             if(el instanceof Array){
17589                 for(var i = 0, len = el.length; i < len; i++) {
17590                         this.register(el[i]);
17591                 }
17592             }else{
17593                 el = Roo.get(el);
17594                 els[el.id] = el;
17595             }
17596         },
17597         
17598         /**
17599          * Unregisters overflow element(s) so they are no longer scrolled
17600          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17601          */
17602         unregister : function(el){
17603             if(el instanceof Array){
17604                 for(var i = 0, len = el.length; i < len; i++) {
17605                         this.unregister(el[i]);
17606                 }
17607             }else{
17608                 el = Roo.get(el);
17609                 delete els[el.id];
17610             }
17611         },
17612         
17613         /**
17614          * The number of pixels from the edge of a container the pointer needs to be to 
17615          * trigger scrolling (defaults to 25)
17616          * @type Number
17617          */
17618         thresh : 25,
17619         
17620         /**
17621          * The number of pixels to scroll in each scroll increment (defaults to 50)
17622          * @type Number
17623          */
17624         increment : 100,
17625         
17626         /**
17627          * The frequency of scrolls in milliseconds (defaults to 500)
17628          * @type Number
17629          */
17630         frequency : 500,
17631         
17632         /**
17633          * True to animate the scroll (defaults to true)
17634          * @type Boolean
17635          */
17636         animate: true,
17637         
17638         /**
17639          * The animation duration in seconds - 
17640          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17641          * @type Number
17642          */
17643         animDuration: .4,
17644         
17645         /**
17646          * Manually trigger a cache refresh.
17647          */
17648         refreshCache : function(){
17649             for(var id in els){
17650                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17651                     els[id]._region = els[id].getRegion();
17652                 }
17653             }
17654         }
17655     };
17656 }();/*
17657  * Based on:
17658  * Ext JS Library 1.1.1
17659  * Copyright(c) 2006-2007, Ext JS, LLC.
17660  *
17661  * Originally Released Under LGPL - original licence link has changed is not relivant.
17662  *
17663  * Fork - LGPL
17664  * <script type="text/javascript">
17665  */
17666  
17667
17668 /**
17669  * @class Roo.dd.Registry
17670  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17671  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17672  * @singleton
17673  */
17674 Roo.dd.Registry = function(){
17675     var elements = {}; 
17676     var handles = {}; 
17677     var autoIdSeed = 0;
17678
17679     var getId = function(el, autogen){
17680         if(typeof el == "string"){
17681             return el;
17682         }
17683         var id = el.id;
17684         if(!id && autogen !== false){
17685             id = "roodd-" + (++autoIdSeed);
17686             el.id = id;
17687         }
17688         return id;
17689     };
17690     
17691     return {
17692     /**
17693      * Register a drag drop element
17694      * @param {String|HTMLElement} element The id or DOM node to register
17695      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17696      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17697      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17698      * populated in the data object (if applicable):
17699      * <pre>
17700 Value      Description<br />
17701 ---------  ------------------------------------------<br />
17702 handles    Array of DOM nodes that trigger dragging<br />
17703            for the element being registered<br />
17704 isHandle   True if the element passed in triggers<br />
17705            dragging itself, else false
17706 </pre>
17707      */
17708         register : function(el, data){
17709             data = data || {};
17710             if(typeof el == "string"){
17711                 el = document.getElementById(el);
17712             }
17713             data.ddel = el;
17714             elements[getId(el)] = data;
17715             if(data.isHandle !== false){
17716                 handles[data.ddel.id] = data;
17717             }
17718             if(data.handles){
17719                 var hs = data.handles;
17720                 for(var i = 0, len = hs.length; i < len; i++){
17721                         handles[getId(hs[i])] = data;
17722                 }
17723             }
17724         },
17725
17726     /**
17727      * Unregister a drag drop element
17728      * @param {String|HTMLElement}  element The id or DOM node to unregister
17729      */
17730         unregister : function(el){
17731             var id = getId(el, false);
17732             var data = elements[id];
17733             if(data){
17734                 delete elements[id];
17735                 if(data.handles){
17736                     var hs = data.handles;
17737                     for(var i = 0, len = hs.length; i < len; i++){
17738                         delete handles[getId(hs[i], false)];
17739                     }
17740                 }
17741             }
17742         },
17743
17744     /**
17745      * Returns the handle registered for a DOM Node by id
17746      * @param {String|HTMLElement} id The DOM node or id to look up
17747      * @return {Object} handle The custom handle data
17748      */
17749         getHandle : function(id){
17750             if(typeof id != "string"){ // must be element?
17751                 id = id.id;
17752             }
17753             return handles[id];
17754         },
17755
17756     /**
17757      * Returns the handle that is registered for the DOM node that is the target of the event
17758      * @param {Event} e The event
17759      * @return {Object} handle The custom handle data
17760      */
17761         getHandleFromEvent : function(e){
17762             var t = Roo.lib.Event.getTarget(e);
17763             return t ? handles[t.id] : null;
17764         },
17765
17766     /**
17767      * Returns a custom data object that is registered for a DOM node by id
17768      * @param {String|HTMLElement} id The DOM node or id to look up
17769      * @return {Object} data The custom data
17770      */
17771         getTarget : function(id){
17772             if(typeof id != "string"){ // must be element?
17773                 id = id.id;
17774             }
17775             return elements[id];
17776         },
17777
17778     /**
17779      * Returns a custom data object that is registered for the DOM node that is the target of the event
17780      * @param {Event} e The event
17781      * @return {Object} data The custom data
17782      */
17783         getTargetFromEvent : function(e){
17784             var t = Roo.lib.Event.getTarget(e);
17785             return t ? elements[t.id] || handles[t.id] : null;
17786         }
17787     };
17788 }();/*
17789  * Based on:
17790  * Ext JS Library 1.1.1
17791  * Copyright(c) 2006-2007, Ext JS, LLC.
17792  *
17793  * Originally Released Under LGPL - original licence link has changed is not relivant.
17794  *
17795  * Fork - LGPL
17796  * <script type="text/javascript">
17797  */
17798  
17799
17800 /**
17801  * @class Roo.dd.StatusProxy
17802  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17803  * default drag proxy used by all Roo.dd components.
17804  * @constructor
17805  * @param {Object} config
17806  */
17807 Roo.dd.StatusProxy = function(config){
17808     Roo.apply(this, config);
17809     this.id = this.id || Roo.id();
17810     this.el = new Roo.Layer({
17811         dh: {
17812             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17813                 {tag: "div", cls: "x-dd-drop-icon"},
17814                 {tag: "div", cls: "x-dd-drag-ghost"}
17815             ]
17816         }, 
17817         shadow: !config || config.shadow !== false
17818     });
17819     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17820     this.dropStatus = this.dropNotAllowed;
17821 };
17822
17823 Roo.dd.StatusProxy.prototype = {
17824     /**
17825      * @cfg {String} dropAllowed
17826      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17827      */
17828     dropAllowed : "x-dd-drop-ok",
17829     /**
17830      * @cfg {String} dropNotAllowed
17831      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17832      */
17833     dropNotAllowed : "x-dd-drop-nodrop",
17834
17835     /**
17836      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17837      * over the current target element.
17838      * @param {String} cssClass The css class for the new drop status indicator image
17839      */
17840     setStatus : function(cssClass){
17841         cssClass = cssClass || this.dropNotAllowed;
17842         if(this.dropStatus != cssClass){
17843             this.el.replaceClass(this.dropStatus, cssClass);
17844             this.dropStatus = cssClass;
17845         }
17846     },
17847
17848     /**
17849      * Resets the status indicator to the default dropNotAllowed value
17850      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17851      */
17852     reset : function(clearGhost){
17853         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17854         this.dropStatus = this.dropNotAllowed;
17855         if(clearGhost){
17856             this.ghost.update("");
17857         }
17858     },
17859
17860     /**
17861      * Updates the contents of the ghost element
17862      * @param {String} html The html that will replace the current innerHTML of the ghost element
17863      */
17864     update : function(html){
17865         if(typeof html == "string"){
17866             this.ghost.update(html);
17867         }else{
17868             this.ghost.update("");
17869             html.style.margin = "0";
17870             this.ghost.dom.appendChild(html);
17871         }
17872         // ensure float = none set?? cant remember why though.
17873         var el = this.ghost.dom.firstChild;
17874                 if(el){
17875                         Roo.fly(el).setStyle('float', 'none');
17876                 }
17877     },
17878     
17879     /**
17880      * Returns the underlying proxy {@link Roo.Layer}
17881      * @return {Roo.Layer} el
17882     */
17883     getEl : function(){
17884         return this.el;
17885     },
17886
17887     /**
17888      * Returns the ghost element
17889      * @return {Roo.Element} el
17890      */
17891     getGhost : function(){
17892         return this.ghost;
17893     },
17894
17895     /**
17896      * Hides the proxy
17897      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17898      */
17899     hide : function(clear){
17900         this.el.hide();
17901         if(clear){
17902             this.reset(true);
17903         }
17904     },
17905
17906     /**
17907      * Stops the repair animation if it's currently running
17908      */
17909     stop : function(){
17910         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17911             this.anim.stop();
17912         }
17913     },
17914
17915     /**
17916      * Displays this proxy
17917      */
17918     show : function(){
17919         this.el.show();
17920     },
17921
17922     /**
17923      * Force the Layer to sync its shadow and shim positions to the element
17924      */
17925     sync : function(){
17926         this.el.sync();
17927     },
17928
17929     /**
17930      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17931      * invalid drop operation by the item being dragged.
17932      * @param {Array} xy The XY position of the element ([x, y])
17933      * @param {Function} callback The function to call after the repair is complete
17934      * @param {Object} scope The scope in which to execute the callback
17935      */
17936     repair : function(xy, callback, scope){
17937         this.callback = callback;
17938         this.scope = scope;
17939         if(xy && this.animRepair !== false){
17940             this.el.addClass("x-dd-drag-repair");
17941             this.el.hideUnders(true);
17942             this.anim = this.el.shift({
17943                 duration: this.repairDuration || .5,
17944                 easing: 'easeOut',
17945                 xy: xy,
17946                 stopFx: true,
17947                 callback: this.afterRepair,
17948                 scope: this
17949             });
17950         }else{
17951             this.afterRepair();
17952         }
17953     },
17954
17955     // private
17956     afterRepair : function(){
17957         this.hide(true);
17958         if(typeof this.callback == "function"){
17959             this.callback.call(this.scope || this);
17960         }
17961         this.callback = null;
17962         this.scope = null;
17963     }
17964 };/*
17965  * Based on:
17966  * Ext JS Library 1.1.1
17967  * Copyright(c) 2006-2007, Ext JS, LLC.
17968  *
17969  * Originally Released Under LGPL - original licence link has changed is not relivant.
17970  *
17971  * Fork - LGPL
17972  * <script type="text/javascript">
17973  */
17974
17975 /**
17976  * @class Roo.dd.DragSource
17977  * @extends Roo.dd.DDProxy
17978  * A simple class that provides the basic implementation needed to make any element draggable.
17979  * @constructor
17980  * @param {String/HTMLElement/Element} el The container element
17981  * @param {Object} config
17982  */
17983 Roo.dd.DragSource = function(el, config){
17984     this.el = Roo.get(el);
17985     this.dragData = {};
17986     
17987     Roo.apply(this, config);
17988     
17989     if(!this.proxy){
17990         this.proxy = new Roo.dd.StatusProxy();
17991     }
17992
17993     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17994           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17995     
17996     this.dragging = false;
17997 };
17998
17999 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18000     /**
18001      * @cfg {String} dropAllowed
18002      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18003      */
18004     dropAllowed : "x-dd-drop-ok",
18005     /**
18006      * @cfg {String} dropNotAllowed
18007      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18008      */
18009     dropNotAllowed : "x-dd-drop-nodrop",
18010
18011     /**
18012      * Returns the data object associated with this drag source
18013      * @return {Object} data An object containing arbitrary data
18014      */
18015     getDragData : function(e){
18016         return this.dragData;
18017     },
18018
18019     // private
18020     onDragEnter : function(e, id){
18021         var target = Roo.dd.DragDropMgr.getDDById(id);
18022         this.cachedTarget = target;
18023         if(this.beforeDragEnter(target, e, id) !== false){
18024             if(target.isNotifyTarget){
18025                 var status = target.notifyEnter(this, e, this.dragData);
18026                 this.proxy.setStatus(status);
18027             }else{
18028                 this.proxy.setStatus(this.dropAllowed);
18029             }
18030             
18031             if(this.afterDragEnter){
18032                 /**
18033                  * An empty function by default, but provided so that you can perform a custom action
18034                  * when the dragged item enters the drop target by providing an implementation.
18035                  * @param {Roo.dd.DragDrop} target The drop target
18036                  * @param {Event} e The event object
18037                  * @param {String} id The id of the dragged element
18038                  * @method afterDragEnter
18039                  */
18040                 this.afterDragEnter(target, e, id);
18041             }
18042         }
18043     },
18044
18045     /**
18046      * An empty function by default, but provided so that you can perform a custom action
18047      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18048      * @param {Roo.dd.DragDrop} target The drop target
18049      * @param {Event} e The event object
18050      * @param {String} id The id of the dragged element
18051      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18052      */
18053     beforeDragEnter : function(target, e, id){
18054         return true;
18055     },
18056
18057     // private
18058     alignElWithMouse: function() {
18059         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18060         this.proxy.sync();
18061     },
18062
18063     // private
18064     onDragOver : function(e, id){
18065         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18066         if(this.beforeDragOver(target, e, id) !== false){
18067             if(target.isNotifyTarget){
18068                 var status = target.notifyOver(this, e, this.dragData);
18069                 this.proxy.setStatus(status);
18070             }
18071
18072             if(this.afterDragOver){
18073                 /**
18074                  * An empty function by default, but provided so that you can perform a custom action
18075                  * while the dragged item is over the drop target by providing an implementation.
18076                  * @param {Roo.dd.DragDrop} target The drop target
18077                  * @param {Event} e The event object
18078                  * @param {String} id The id of the dragged element
18079                  * @method afterDragOver
18080                  */
18081                 this.afterDragOver(target, e, id);
18082             }
18083         }
18084     },
18085
18086     /**
18087      * An empty function by default, but provided so that you can perform a custom action
18088      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18089      * @param {Roo.dd.DragDrop} target The drop target
18090      * @param {Event} e The event object
18091      * @param {String} id The id of the dragged element
18092      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18093      */
18094     beforeDragOver : function(target, e, id){
18095         return true;
18096     },
18097
18098     // private
18099     onDragOut : function(e, id){
18100         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18101         if(this.beforeDragOut(target, e, id) !== false){
18102             if(target.isNotifyTarget){
18103                 target.notifyOut(this, e, this.dragData);
18104             }
18105             this.proxy.reset();
18106             if(this.afterDragOut){
18107                 /**
18108                  * An empty function by default, but provided so that you can perform a custom action
18109                  * after the dragged item is dragged out of the target without dropping.
18110                  * @param {Roo.dd.DragDrop} target The drop target
18111                  * @param {Event} e The event object
18112                  * @param {String} id The id of the dragged element
18113                  * @method afterDragOut
18114                  */
18115                 this.afterDragOut(target, e, id);
18116             }
18117         }
18118         this.cachedTarget = null;
18119     },
18120
18121     /**
18122      * An empty function by default, but provided so that you can perform a custom action before the dragged
18123      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18124      * @param {Roo.dd.DragDrop} target The drop target
18125      * @param {Event} e The event object
18126      * @param {String} id The id of the dragged element
18127      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18128      */
18129     beforeDragOut : function(target, e, id){
18130         return true;
18131     },
18132     
18133     // private
18134     onDragDrop : function(e, id){
18135         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18136         if(this.beforeDragDrop(target, e, id) !== false){
18137             if(target.isNotifyTarget){
18138                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18139                     this.onValidDrop(target, e, id);
18140                 }else{
18141                     this.onInvalidDrop(target, e, id);
18142                 }
18143             }else{
18144                 this.onValidDrop(target, e, id);
18145             }
18146             
18147             if(this.afterDragDrop){
18148                 /**
18149                  * An empty function by default, but provided so that you can perform a custom action
18150                  * after a valid drag drop has occurred by providing an implementation.
18151                  * @param {Roo.dd.DragDrop} target The drop target
18152                  * @param {Event} e The event object
18153                  * @param {String} id The id of the dropped element
18154                  * @method afterDragDrop
18155                  */
18156                 this.afterDragDrop(target, e, id);
18157             }
18158         }
18159         delete this.cachedTarget;
18160     },
18161
18162     /**
18163      * An empty function by default, but provided so that you can perform a custom action before the dragged
18164      * item is dropped onto the target and optionally cancel the onDragDrop.
18165      * @param {Roo.dd.DragDrop} target The drop target
18166      * @param {Event} e The event object
18167      * @param {String} id The id of the dragged element
18168      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18169      */
18170     beforeDragDrop : function(target, e, id){
18171         return true;
18172     },
18173
18174     // private
18175     onValidDrop : function(target, e, id){
18176         this.hideProxy();
18177         if(this.afterValidDrop){
18178             /**
18179              * An empty function by default, but provided so that you can perform a custom action
18180              * after a valid drop has occurred by providing an implementation.
18181              * @param {Object} target The target DD 
18182              * @param {Event} e The event object
18183              * @param {String} id The id of the dropped element
18184              * @method afterInvalidDrop
18185              */
18186             this.afterValidDrop(target, e, id);
18187         }
18188     },
18189
18190     // private
18191     getRepairXY : function(e, data){
18192         return this.el.getXY();  
18193     },
18194
18195     // private
18196     onInvalidDrop : function(target, e, id){
18197         this.beforeInvalidDrop(target, e, id);
18198         if(this.cachedTarget){
18199             if(this.cachedTarget.isNotifyTarget){
18200                 this.cachedTarget.notifyOut(this, e, this.dragData);
18201             }
18202             this.cacheTarget = null;
18203         }
18204         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18205
18206         if(this.afterInvalidDrop){
18207             /**
18208              * An empty function by default, but provided so that you can perform a custom action
18209              * after an invalid drop has occurred by providing an implementation.
18210              * @param {Event} e The event object
18211              * @param {String} id The id of the dropped element
18212              * @method afterInvalidDrop
18213              */
18214             this.afterInvalidDrop(e, id);
18215         }
18216     },
18217
18218     // private
18219     afterRepair : function(){
18220         if(Roo.enableFx){
18221             this.el.highlight(this.hlColor || "c3daf9");
18222         }
18223         this.dragging = false;
18224     },
18225
18226     /**
18227      * An empty function by default, but provided so that you can perform a custom action after an invalid
18228      * drop has occurred.
18229      * @param {Roo.dd.DragDrop} target The drop target
18230      * @param {Event} e The event object
18231      * @param {String} id The id of the dragged element
18232      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18233      */
18234     beforeInvalidDrop : function(target, e, id){
18235         return true;
18236     },
18237
18238     // private
18239     handleMouseDown : function(e){
18240         if(this.dragging) {
18241             return;
18242         }
18243         var data = this.getDragData(e);
18244         if(data && this.onBeforeDrag(data, e) !== false){
18245             this.dragData = data;
18246             this.proxy.stop();
18247             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18248         } 
18249     },
18250
18251     /**
18252      * An empty function by default, but provided so that you can perform a custom action before the initial
18253      * drag event begins and optionally cancel it.
18254      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18255      * @param {Event} e The event object
18256      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18257      */
18258     onBeforeDrag : function(data, e){
18259         return true;
18260     },
18261
18262     /**
18263      * An empty function by default, but provided so that you can perform a custom action once the initial
18264      * drag event has begun.  The drag cannot be canceled from this function.
18265      * @param {Number} x The x position of the click on the dragged object
18266      * @param {Number} y The y position of the click on the dragged object
18267      */
18268     onStartDrag : Roo.emptyFn,
18269
18270     // private - YUI override
18271     startDrag : function(x, y){
18272         this.proxy.reset();
18273         this.dragging = true;
18274         this.proxy.update("");
18275         this.onInitDrag(x, y);
18276         this.proxy.show();
18277     },
18278
18279     // private
18280     onInitDrag : function(x, y){
18281         var clone = this.el.dom.cloneNode(true);
18282         clone.id = Roo.id(); // prevent duplicate ids
18283         this.proxy.update(clone);
18284         this.onStartDrag(x, y);
18285         return true;
18286     },
18287
18288     /**
18289      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18290      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18291      */
18292     getProxy : function(){
18293         return this.proxy;  
18294     },
18295
18296     /**
18297      * Hides the drag source's {@link Roo.dd.StatusProxy}
18298      */
18299     hideProxy : function(){
18300         this.proxy.hide();  
18301         this.proxy.reset(true);
18302         this.dragging = false;
18303     },
18304
18305     // private
18306     triggerCacheRefresh : function(){
18307         Roo.dd.DDM.refreshCache(this.groups);
18308     },
18309
18310     // private - override to prevent hiding
18311     b4EndDrag: function(e) {
18312     },
18313
18314     // private - override to prevent moving
18315     endDrag : function(e){
18316         this.onEndDrag(this.dragData, e);
18317     },
18318
18319     // private
18320     onEndDrag : function(data, e){
18321     },
18322     
18323     // private - pin to cursor
18324     autoOffset : function(x, y) {
18325         this.setDelta(-12, -20);
18326     }    
18327 });/*
18328  * Based on:
18329  * Ext JS Library 1.1.1
18330  * Copyright(c) 2006-2007, Ext JS, LLC.
18331  *
18332  * Originally Released Under LGPL - original licence link has changed is not relivant.
18333  *
18334  * Fork - LGPL
18335  * <script type="text/javascript">
18336  */
18337
18338
18339 /**
18340  * @class Roo.dd.DropTarget
18341  * @extends Roo.dd.DDTarget
18342  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18343  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18344  * @constructor
18345  * @param {String/HTMLElement/Element} el The container element
18346  * @param {Object} config
18347  */
18348 Roo.dd.DropTarget = function(el, config){
18349     this.el = Roo.get(el);
18350     
18351     var listeners = false; ;
18352     if (config && config.listeners) {
18353         listeners= config.listeners;
18354         delete config.listeners;
18355     }
18356     Roo.apply(this, config);
18357     
18358     if(this.containerScroll){
18359         Roo.dd.ScrollManager.register(this.el);
18360     }
18361     this.addEvents( {
18362          /**
18363          * @scope Roo.dd.DropTarget
18364          */
18365          
18366          /**
18367          * @event enter
18368          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18369          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18370          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18371          * 
18372          * IMPORTANT : it should set this.overClass and this.dropAllowed
18373          * 
18374          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18375          * @param {Event} e The event
18376          * @param {Object} data An object containing arbitrary data supplied by the drag source
18377          */
18378         "enter" : true,
18379         
18380          /**
18381          * @event over
18382          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18383          * This method will be called on every mouse movement while the drag source is over the drop target.
18384          * This default implementation simply returns the dropAllowed config value.
18385          * 
18386          * IMPORTANT : it should set this.dropAllowed
18387          * 
18388          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18389          * @param {Event} e The event
18390          * @param {Object} data An object containing arbitrary data supplied by the drag source
18391          
18392          */
18393         "over" : true,
18394         /**
18395          * @event out
18396          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18397          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18398          * overClass (if any) from the drop element.
18399          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18400          * @param {Event} e The event
18401          * @param {Object} data An object containing arbitrary data supplied by the drag source
18402          */
18403          "out" : true,
18404          
18405         /**
18406          * @event drop
18407          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18408          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18409          * implementation that does something to process the drop event and returns true so that the drag source's
18410          * repair action does not run.
18411          * 
18412          * IMPORTANT : it should set this.success
18413          * 
18414          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18415          * @param {Event} e The event
18416          * @param {Object} data An object containing arbitrary data supplied by the drag source
18417         */
18418          "drop" : true
18419     });
18420             
18421      
18422     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18423         this.el.dom, 
18424         this.ddGroup || this.group,
18425         {
18426             isTarget: true,
18427             listeners : listeners || {} 
18428            
18429         
18430         }
18431     );
18432
18433 };
18434
18435 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18436     /**
18437      * @cfg {String} overClass
18438      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18439      */
18440      /**
18441      * @cfg {String} ddGroup
18442      * The drag drop group to handle drop events for
18443      */
18444      
18445     /**
18446      * @cfg {String} dropAllowed
18447      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18448      */
18449     dropAllowed : "x-dd-drop-ok",
18450     /**
18451      * @cfg {String} dropNotAllowed
18452      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18453      */
18454     dropNotAllowed : "x-dd-drop-nodrop",
18455     /**
18456      * @cfg {boolean} success
18457      * set this after drop listener.. 
18458      */
18459     success : false,
18460     /**
18461      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18462      * if the drop point is valid for over/enter..
18463      */
18464     valid : false,
18465     // private
18466     isTarget : true,
18467
18468     // private
18469     isNotifyTarget : true,
18470     
18471     /**
18472      * @hide
18473      */
18474     notifyEnter : function(dd, e, data)
18475     {
18476         this.valid = true;
18477         this.fireEvent('enter', dd, e, data);
18478         if(this.overClass){
18479             this.el.addClass(this.overClass);
18480         }
18481         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18482             this.valid ? this.dropAllowed : this.dropNotAllowed
18483         );
18484     },
18485
18486     /**
18487      * @hide
18488      */
18489     notifyOver : function(dd, e, data)
18490     {
18491         this.valid = true;
18492         this.fireEvent('over', dd, e, data);
18493         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18494             this.valid ? this.dropAllowed : this.dropNotAllowed
18495         );
18496     },
18497
18498     /**
18499      * @hide
18500      */
18501     notifyOut : function(dd, e, data)
18502     {
18503         this.fireEvent('out', dd, e, data);
18504         if(this.overClass){
18505             this.el.removeClass(this.overClass);
18506         }
18507     },
18508
18509     /**
18510      * @hide
18511      */
18512     notifyDrop : function(dd, e, data)
18513     {
18514         this.success = false;
18515         this.fireEvent('drop', dd, e, data);
18516         return this.success;
18517     }
18518 });/*
18519  * Based on:
18520  * Ext JS Library 1.1.1
18521  * Copyright(c) 2006-2007, Ext JS, LLC.
18522  *
18523  * Originally Released Under LGPL - original licence link has changed is not relivant.
18524  *
18525  * Fork - LGPL
18526  * <script type="text/javascript">
18527  */
18528
18529
18530 /**
18531  * @class Roo.dd.DragZone
18532  * @extends Roo.dd.DragSource
18533  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18534  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18535  * @constructor
18536  * @param {String/HTMLElement/Element} el The container element
18537  * @param {Object} config
18538  */
18539 Roo.dd.DragZone = function(el, config){
18540     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18541     if(this.containerScroll){
18542         Roo.dd.ScrollManager.register(this.el);
18543     }
18544 };
18545
18546 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18547     /**
18548      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18549      * for auto scrolling during drag operations.
18550      */
18551     /**
18552      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18553      * method after a failed drop (defaults to "c3daf9" - light blue)
18554      */
18555
18556     /**
18557      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18558      * for a valid target to drag based on the mouse down. Override this method
18559      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18560      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18561      * @param {EventObject} e The mouse down event
18562      * @return {Object} The dragData
18563      */
18564     getDragData : function(e){
18565         return Roo.dd.Registry.getHandleFromEvent(e);
18566     },
18567     
18568     /**
18569      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18570      * this.dragData.ddel
18571      * @param {Number} x The x position of the click on the dragged object
18572      * @param {Number} y The y position of the click on the dragged object
18573      * @return {Boolean} true to continue the drag, false to cancel
18574      */
18575     onInitDrag : function(x, y){
18576         this.proxy.update(this.dragData.ddel.cloneNode(true));
18577         this.onStartDrag(x, y);
18578         return true;
18579     },
18580     
18581     /**
18582      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18583      */
18584     afterRepair : function(){
18585         if(Roo.enableFx){
18586             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18587         }
18588         this.dragging = false;
18589     },
18590
18591     /**
18592      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18593      * the XY of this.dragData.ddel
18594      * @param {EventObject} e The mouse up event
18595      * @return {Array} The xy location (e.g. [100, 200])
18596      */
18597     getRepairXY : function(e){
18598         return Roo.Element.fly(this.dragData.ddel).getXY();  
18599     }
18600 });/*
18601  * Based on:
18602  * Ext JS Library 1.1.1
18603  * Copyright(c) 2006-2007, Ext JS, LLC.
18604  *
18605  * Originally Released Under LGPL - original licence link has changed is not relivant.
18606  *
18607  * Fork - LGPL
18608  * <script type="text/javascript">
18609  */
18610 /**
18611  * @class Roo.dd.DropZone
18612  * @extends Roo.dd.DropTarget
18613  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18614  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18615  * @constructor
18616  * @param {String/HTMLElement/Element} el The container element
18617  * @param {Object} config
18618  */
18619 Roo.dd.DropZone = function(el, config){
18620     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18621 };
18622
18623 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18624     /**
18625      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18626      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18627      * provide your own custom lookup.
18628      * @param {Event} e The event
18629      * @return {Object} data The custom data
18630      */
18631     getTargetFromEvent : function(e){
18632         return Roo.dd.Registry.getTargetFromEvent(e);
18633     },
18634
18635     /**
18636      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18637      * that it has registered.  This method has no default implementation and should be overridden to provide
18638      * node-specific processing if necessary.
18639      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18640      * {@link #getTargetFromEvent} for this node)
18641      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18642      * @param {Event} e The event
18643      * @param {Object} data An object containing arbitrary data supplied by the drag source
18644      */
18645     onNodeEnter : function(n, dd, e, data){
18646         
18647     },
18648
18649     /**
18650      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18651      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18652      * overridden to provide the proper feedback.
18653      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18654      * {@link #getTargetFromEvent} for this node)
18655      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18656      * @param {Event} e The event
18657      * @param {Object} data An object containing arbitrary data supplied by the drag source
18658      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18659      * underlying {@link Roo.dd.StatusProxy} can be updated
18660      */
18661     onNodeOver : function(n, dd, e, data){
18662         return this.dropAllowed;
18663     },
18664
18665     /**
18666      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18667      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18668      * node-specific processing if necessary.
18669      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18670      * {@link #getTargetFromEvent} for this node)
18671      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18672      * @param {Event} e The event
18673      * @param {Object} data An object containing arbitrary data supplied by the drag source
18674      */
18675     onNodeOut : function(n, dd, e, data){
18676         
18677     },
18678
18679     /**
18680      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18681      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18682      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18683      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18684      * {@link #getTargetFromEvent} for this node)
18685      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18686      * @param {Event} e The event
18687      * @param {Object} data An object containing arbitrary data supplied by the drag source
18688      * @return {Boolean} True if the drop was valid, else false
18689      */
18690     onNodeDrop : function(n, dd, e, data){
18691         return false;
18692     },
18693
18694     /**
18695      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18696      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18697      * it should be overridden to provide the proper feedback if necessary.
18698      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18699      * @param {Event} e The event
18700      * @param {Object} data An object containing arbitrary data supplied by the drag source
18701      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18702      * underlying {@link Roo.dd.StatusProxy} can be updated
18703      */
18704     onContainerOver : function(dd, e, data){
18705         return this.dropNotAllowed;
18706     },
18707
18708     /**
18709      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18710      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18711      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18712      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18713      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18714      * @param {Event} e The event
18715      * @param {Object} data An object containing arbitrary data supplied by the drag source
18716      * @return {Boolean} True if the drop was valid, else false
18717      */
18718     onContainerDrop : function(dd, e, data){
18719         return false;
18720     },
18721
18722     /**
18723      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18724      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18725      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18726      * you should override this method and provide a custom implementation.
18727      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18728      * @param {Event} e The event
18729      * @param {Object} data An object containing arbitrary data supplied by the drag source
18730      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18731      * underlying {@link Roo.dd.StatusProxy} can be updated
18732      */
18733     notifyEnter : function(dd, e, data){
18734         return this.dropNotAllowed;
18735     },
18736
18737     /**
18738      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18739      * This method will be called on every mouse movement while the drag source is over the drop zone.
18740      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18741      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18742      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18743      * registered node, it will call {@link #onContainerOver}.
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     notifyOver : function(dd, e, data){
18751         var n = this.getTargetFromEvent(e);
18752         if(!n){ // not over valid drop target
18753             if(this.lastOverNode){
18754                 this.onNodeOut(this.lastOverNode, dd, e, data);
18755                 this.lastOverNode = null;
18756             }
18757             return this.onContainerOver(dd, e, data);
18758         }
18759         if(this.lastOverNode != n){
18760             if(this.lastOverNode){
18761                 this.onNodeOut(this.lastOverNode, dd, e, data);
18762             }
18763             this.onNodeEnter(n, dd, e, data);
18764             this.lastOverNode = n;
18765         }
18766         return this.onNodeOver(n, dd, e, data);
18767     },
18768
18769     /**
18770      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18771      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18772      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18773      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18774      * @param {Event} e The event
18775      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18776      */
18777     notifyOut : function(dd, e, data){
18778         if(this.lastOverNode){
18779             this.onNodeOut(this.lastOverNode, dd, e, data);
18780             this.lastOverNode = null;
18781         }
18782     },
18783
18784     /**
18785      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18786      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18787      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18788      * otherwise it will call {@link #onContainerDrop}.
18789      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18790      * @param {Event} e The event
18791      * @param {Object} data An object containing arbitrary data supplied by the drag source
18792      * @return {Boolean} True if the drop was valid, else false
18793      */
18794     notifyDrop : function(dd, e, data){
18795         if(this.lastOverNode){
18796             this.onNodeOut(this.lastOverNode, dd, e, data);
18797             this.lastOverNode = null;
18798         }
18799         var n = this.getTargetFromEvent(e);
18800         return n ?
18801             this.onNodeDrop(n, dd, e, data) :
18802             this.onContainerDrop(dd, e, data);
18803     },
18804
18805     // private
18806     triggerCacheRefresh : function(){
18807         Roo.dd.DDM.refreshCache(this.groups);
18808     }  
18809 });/*
18810  * Based on:
18811  * Ext JS Library 1.1.1
18812  * Copyright(c) 2006-2007, Ext JS, LLC.
18813  *
18814  * Originally Released Under LGPL - original licence link has changed is not relivant.
18815  *
18816  * Fork - LGPL
18817  * <script type="text/javascript">
18818  */
18819
18820
18821 /**
18822  * @class Roo.data.SortTypes
18823  * @singleton
18824  * Defines the default sorting (casting?) comparison functions used when sorting data.
18825  */
18826 Roo.data.SortTypes = {
18827     /**
18828      * Default sort that does nothing
18829      * @param {Mixed} s The value being converted
18830      * @return {Mixed} The comparison value
18831      */
18832     none : function(s){
18833         return s;
18834     },
18835     
18836     /**
18837      * The regular expression used to strip tags
18838      * @type {RegExp}
18839      * @property
18840      */
18841     stripTagsRE : /<\/?[^>]+>/gi,
18842     
18843     /**
18844      * Strips all HTML tags to sort on text only
18845      * @param {Mixed} s The value being converted
18846      * @return {String} The comparison value
18847      */
18848     asText : function(s){
18849         return String(s).replace(this.stripTagsRE, "");
18850     },
18851     
18852     /**
18853      * Strips all HTML tags to sort on text only - Case insensitive
18854      * @param {Mixed} s The value being converted
18855      * @return {String} The comparison value
18856      */
18857     asUCText : function(s){
18858         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18859     },
18860     
18861     /**
18862      * Case insensitive string
18863      * @param {Mixed} s The value being converted
18864      * @return {String} The comparison value
18865      */
18866     asUCString : function(s) {
18867         return String(s).toUpperCase();
18868     },
18869     
18870     /**
18871      * Date sorting
18872      * @param {Mixed} s The value being converted
18873      * @return {Number} The comparison value
18874      */
18875     asDate : function(s) {
18876         if(!s){
18877             return 0;
18878         }
18879         if(s instanceof Date){
18880             return s.getTime();
18881         }
18882         return Date.parse(String(s));
18883     },
18884     
18885     /**
18886      * Float sorting
18887      * @param {Mixed} s The value being converted
18888      * @return {Float} The comparison value
18889      */
18890     asFloat : function(s) {
18891         var val = parseFloat(String(s).replace(/,/g, ""));
18892         if(isNaN(val)) val = 0;
18893         return val;
18894     },
18895     
18896     /**
18897      * Integer sorting
18898      * @param {Mixed} s The value being converted
18899      * @return {Number} The comparison value
18900      */
18901     asInt : function(s) {
18902         var val = parseInt(String(s).replace(/,/g, ""));
18903         if(isNaN(val)) val = 0;
18904         return val;
18905     }
18906 };/*
18907  * Based on:
18908  * Ext JS Library 1.1.1
18909  * Copyright(c) 2006-2007, Ext JS, LLC.
18910  *
18911  * Originally Released Under LGPL - original licence link has changed is not relivant.
18912  *
18913  * Fork - LGPL
18914  * <script type="text/javascript">
18915  */
18916
18917 /**
18918 * @class Roo.data.Record
18919  * Instances of this class encapsulate both record <em>definition</em> information, and record
18920  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18921  * to access Records cached in an {@link Roo.data.Store} object.<br>
18922  * <p>
18923  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18924  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18925  * objects.<br>
18926  * <p>
18927  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18928  * @constructor
18929  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18930  * {@link #create}. The parameters are the same.
18931  * @param {Array} data An associative Array of data values keyed by the field name.
18932  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18933  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18934  * not specified an integer id is generated.
18935  */
18936 Roo.data.Record = function(data, id){
18937     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18938     this.data = data;
18939 };
18940
18941 /**
18942  * Generate a constructor for a specific record layout.
18943  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18944  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18945  * Each field definition object may contain the following properties: <ul>
18946  * <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,
18947  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18948  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18949  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18950  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18951  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18952  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18953  * this may be omitted.</p></li>
18954  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18955  * <ul><li>auto (Default, implies no conversion)</li>
18956  * <li>string</li>
18957  * <li>int</li>
18958  * <li>float</li>
18959  * <li>boolean</li>
18960  * <li>date</li></ul></p></li>
18961  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18962  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18963  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18964  * by the Reader into an object that will be stored in the Record. It is passed the
18965  * following parameters:<ul>
18966  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18967  * </ul></p></li>
18968  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18969  * </ul>
18970  * <br>usage:<br><pre><code>
18971 var TopicRecord = Roo.data.Record.create(
18972     {name: 'title', mapping: 'topic_title'},
18973     {name: 'author', mapping: 'username'},
18974     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18975     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18976     {name: 'lastPoster', mapping: 'user2'},
18977     {name: 'excerpt', mapping: 'post_text'}
18978 );
18979
18980 var myNewRecord = new TopicRecord({
18981     title: 'Do my job please',
18982     author: 'noobie',
18983     totalPosts: 1,
18984     lastPost: new Date(),
18985     lastPoster: 'Animal',
18986     excerpt: 'No way dude!'
18987 });
18988 myStore.add(myNewRecord);
18989 </code></pre>
18990  * @method create
18991  * @static
18992  */
18993 Roo.data.Record.create = function(o){
18994     var f = function(){
18995         f.superclass.constructor.apply(this, arguments);
18996     };
18997     Roo.extend(f, Roo.data.Record);
18998     var p = f.prototype;
18999     p.fields = new Roo.util.MixedCollection(false, function(field){
19000         return field.name;
19001     });
19002     for(var i = 0, len = o.length; i < len; i++){
19003         p.fields.add(new Roo.data.Field(o[i]));
19004     }
19005     f.getField = function(name){
19006         return p.fields.get(name);  
19007     };
19008     return f;
19009 };
19010
19011 Roo.data.Record.AUTO_ID = 1000;
19012 Roo.data.Record.EDIT = 'edit';
19013 Roo.data.Record.REJECT = 'reject';
19014 Roo.data.Record.COMMIT = 'commit';
19015
19016 Roo.data.Record.prototype = {
19017     /**
19018      * Readonly flag - true if this record has been modified.
19019      * @type Boolean
19020      */
19021     dirty : false,
19022     editing : false,
19023     error: null,
19024     modified: null,
19025
19026     // private
19027     join : function(store){
19028         this.store = store;
19029     },
19030
19031     /**
19032      * Set the named field to the specified value.
19033      * @param {String} name The name of the field to set.
19034      * @param {Object} value The value to set the field to.
19035      */
19036     set : function(name, value){
19037         if(this.data[name] == value){
19038             return;
19039         }
19040         this.dirty = true;
19041         if(!this.modified){
19042             this.modified = {};
19043         }
19044         if(typeof this.modified[name] == 'undefined'){
19045             this.modified[name] = this.data[name];
19046         }
19047         this.data[name] = value;
19048         if(!this.editing && this.store){
19049             this.store.afterEdit(this);
19050         }       
19051     },
19052
19053     /**
19054      * Get the value of the named field.
19055      * @param {String} name The name of the field to get the value of.
19056      * @return {Object} The value of the field.
19057      */
19058     get : function(name){
19059         return this.data[name]; 
19060     },
19061
19062     // private
19063     beginEdit : function(){
19064         this.editing = true;
19065         this.modified = {}; 
19066     },
19067
19068     // private
19069     cancelEdit : function(){
19070         this.editing = false;
19071         delete this.modified;
19072     },
19073
19074     // private
19075     endEdit : function(){
19076         this.editing = false;
19077         if(this.dirty && this.store){
19078             this.store.afterEdit(this);
19079         }
19080     },
19081
19082     /**
19083      * Usually called by the {@link Roo.data.Store} which owns the Record.
19084      * Rejects all changes made to the Record since either creation, or the last commit operation.
19085      * Modified fields are reverted to their original values.
19086      * <p>
19087      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19088      * of reject operations.
19089      */
19090     reject : function(){
19091         var m = this.modified;
19092         for(var n in m){
19093             if(typeof m[n] != "function"){
19094                 this.data[n] = m[n];
19095             }
19096         }
19097         this.dirty = false;
19098         delete this.modified;
19099         this.editing = false;
19100         if(this.store){
19101             this.store.afterReject(this);
19102         }
19103     },
19104
19105     /**
19106      * Usually called by the {@link Roo.data.Store} which owns the Record.
19107      * Commits all changes made to the Record since either creation, or the last commit operation.
19108      * <p>
19109      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19110      * of commit operations.
19111      */
19112     commit : function(){
19113         this.dirty = false;
19114         delete this.modified;
19115         this.editing = false;
19116         if(this.store){
19117             this.store.afterCommit(this);
19118         }
19119     },
19120
19121     // private
19122     hasError : function(){
19123         return this.error != null;
19124     },
19125
19126     // private
19127     clearError : function(){
19128         this.error = null;
19129     },
19130
19131     /**
19132      * Creates a copy of this record.
19133      * @param {String} id (optional) A new record id if you don't want to use this record's id
19134      * @return {Record}
19135      */
19136     copy : function(newId) {
19137         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19138     }
19139 };/*
19140  * Based on:
19141  * Ext JS Library 1.1.1
19142  * Copyright(c) 2006-2007, Ext JS, LLC.
19143  *
19144  * Originally Released Under LGPL - original licence link has changed is not relivant.
19145  *
19146  * Fork - LGPL
19147  * <script type="text/javascript">
19148  */
19149
19150
19151
19152 /**
19153  * @class Roo.data.Store
19154  * @extends Roo.util.Observable
19155  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19156  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19157  * <p>
19158  * 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
19159  * has no knowledge of the format of the data returned by the Proxy.<br>
19160  * <p>
19161  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19162  * instances from the data object. These records are cached and made available through accessor functions.
19163  * @constructor
19164  * Creates a new Store.
19165  * @param {Object} config A config object containing the objects needed for the Store to access data,
19166  * and read the data into Records.
19167  */
19168 Roo.data.Store = function(config){
19169     this.data = new Roo.util.MixedCollection(false);
19170     this.data.getKey = function(o){
19171         return o.id;
19172     };
19173     this.baseParams = {};
19174     // private
19175     this.paramNames = {
19176         "start" : "start",
19177         "limit" : "limit",
19178         "sort" : "sort",
19179         "dir" : "dir",
19180         "multisort" : "_multisort"
19181     };
19182
19183     if(config && config.data){
19184         this.inlineData = config.data;
19185         delete config.data;
19186     }
19187
19188     Roo.apply(this, config);
19189     
19190     if(this.reader){ // reader passed
19191         this.reader = Roo.factory(this.reader, Roo.data);
19192         this.reader.xmodule = this.xmodule || false;
19193         if(!this.recordType){
19194             this.recordType = this.reader.recordType;
19195         }
19196         if(this.reader.onMetaChange){
19197             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19198         }
19199     }
19200
19201     if(this.recordType){
19202         this.fields = this.recordType.prototype.fields;
19203     }
19204     this.modified = [];
19205
19206     this.addEvents({
19207         /**
19208          * @event datachanged
19209          * Fires when the data cache has changed, and a widget which is using this Store
19210          * as a Record cache should refresh its view.
19211          * @param {Store} this
19212          */
19213         datachanged : true,
19214         /**
19215          * @event metachange
19216          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19217          * @param {Store} this
19218          * @param {Object} meta The JSON metadata
19219          */
19220         metachange : true,
19221         /**
19222          * @event add
19223          * Fires when Records have been added to the Store
19224          * @param {Store} this
19225          * @param {Roo.data.Record[]} records The array of Records added
19226          * @param {Number} index The index at which the record(s) were added
19227          */
19228         add : true,
19229         /**
19230          * @event remove
19231          * Fires when a Record has been removed from the Store
19232          * @param {Store} this
19233          * @param {Roo.data.Record} record The Record that was removed
19234          * @param {Number} index The index at which the record was removed
19235          */
19236         remove : true,
19237         /**
19238          * @event update
19239          * Fires when a Record has been updated
19240          * @param {Store} this
19241          * @param {Roo.data.Record} record The Record that was updated
19242          * @param {String} operation The update operation being performed.  Value may be one of:
19243          * <pre><code>
19244  Roo.data.Record.EDIT
19245  Roo.data.Record.REJECT
19246  Roo.data.Record.COMMIT
19247          * </code></pre>
19248          */
19249         update : true,
19250         /**
19251          * @event clear
19252          * Fires when the data cache has been cleared.
19253          * @param {Store} this
19254          */
19255         clear : true,
19256         /**
19257          * @event beforeload
19258          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19259          * the load action will be canceled.
19260          * @param {Store} this
19261          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19262          */
19263         beforeload : true,
19264         /**
19265          * @event load
19266          * Fires after a new set of Records has been loaded.
19267          * @param {Store} this
19268          * @param {Roo.data.Record[]} records The Records that were loaded
19269          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19270          */
19271         load : true,
19272         /**
19273          * @event loadexception
19274          * Fires if an exception occurs in the Proxy during loading.
19275          * Called with the signature of the Proxy's "loadexception" event.
19276          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19277          * 
19278          * @param {Proxy} 
19279          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19280          * @param {Object} load options 
19281          * @param {Object} jsonData from your request (normally this contains the Exception)
19282          */
19283         loadexception : true
19284     });
19285     
19286     if(this.proxy){
19287         this.proxy = Roo.factory(this.proxy, Roo.data);
19288         this.proxy.xmodule = this.xmodule || false;
19289         this.relayEvents(this.proxy,  ["loadexception"]);
19290     }
19291     this.sortToggle = {};
19292     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19293
19294     Roo.data.Store.superclass.constructor.call(this);
19295
19296     if(this.inlineData){
19297         this.loadData(this.inlineData);
19298         delete this.inlineData;
19299     }
19300 };
19301 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19302      /**
19303     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19304     * without a remote query - used by combo/forms at present.
19305     */
19306     
19307     /**
19308     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19309     */
19310     /**
19311     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19312     */
19313     /**
19314     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19315     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19316     */
19317     /**
19318     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19319     * on any HTTP request
19320     */
19321     /**
19322     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19323     */
19324     /**
19325     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19326     */
19327     multiSort: false,
19328     /**
19329     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19330     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19331     */
19332     remoteSort : false,
19333
19334     /**
19335     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19336      * loaded or when a record is removed. (defaults to false).
19337     */
19338     pruneModifiedRecords : false,
19339
19340     // private
19341     lastOptions : null,
19342
19343     /**
19344      * Add Records to the Store and fires the add event.
19345      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19346      */
19347     add : function(records){
19348         records = [].concat(records);
19349         for(var i = 0, len = records.length; i < len; i++){
19350             records[i].join(this);
19351         }
19352         var index = this.data.length;
19353         this.data.addAll(records);
19354         this.fireEvent("add", this, records, index);
19355     },
19356
19357     /**
19358      * Remove a Record from the Store and fires the remove event.
19359      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19360      */
19361     remove : function(record){
19362         var index = this.data.indexOf(record);
19363         this.data.removeAt(index);
19364         if(this.pruneModifiedRecords){
19365             this.modified.remove(record);
19366         }
19367         this.fireEvent("remove", this, record, index);
19368     },
19369
19370     /**
19371      * Remove all Records from the Store and fires the clear event.
19372      */
19373     removeAll : function(){
19374         this.data.clear();
19375         if(this.pruneModifiedRecords){
19376             this.modified = [];
19377         }
19378         this.fireEvent("clear", this);
19379     },
19380
19381     /**
19382      * Inserts Records to the Store at the given index and fires the add event.
19383      * @param {Number} index The start index at which to insert the passed Records.
19384      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19385      */
19386     insert : function(index, records){
19387         records = [].concat(records);
19388         for(var i = 0, len = records.length; i < len; i++){
19389             this.data.insert(index, records[i]);
19390             records[i].join(this);
19391         }
19392         this.fireEvent("add", this, records, index);
19393     },
19394
19395     /**
19396      * Get the index within the cache of the passed Record.
19397      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19398      * @return {Number} The index of the passed Record. Returns -1 if not found.
19399      */
19400     indexOf : function(record){
19401         return this.data.indexOf(record);
19402     },
19403
19404     /**
19405      * Get the index within the cache of the Record with the passed id.
19406      * @param {String} id The id of the Record to find.
19407      * @return {Number} The index of the Record. Returns -1 if not found.
19408      */
19409     indexOfId : function(id){
19410         return this.data.indexOfKey(id);
19411     },
19412
19413     /**
19414      * Get the Record with the specified id.
19415      * @param {String} id The id of the Record to find.
19416      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19417      */
19418     getById : function(id){
19419         return this.data.key(id);
19420     },
19421
19422     /**
19423      * Get the Record at the specified index.
19424      * @param {Number} index The index of the Record to find.
19425      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19426      */
19427     getAt : function(index){
19428         return this.data.itemAt(index);
19429     },
19430
19431     /**
19432      * Returns a range of Records between specified indices.
19433      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19434      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19435      * @return {Roo.data.Record[]} An array of Records
19436      */
19437     getRange : function(start, end){
19438         return this.data.getRange(start, end);
19439     },
19440
19441     // private
19442     storeOptions : function(o){
19443         o = Roo.apply({}, o);
19444         delete o.callback;
19445         delete o.scope;
19446         this.lastOptions = o;
19447     },
19448
19449     /**
19450      * Loads the Record cache from the configured Proxy using the configured Reader.
19451      * <p>
19452      * If using remote paging, then the first load call must specify the <em>start</em>
19453      * and <em>limit</em> properties in the options.params property to establish the initial
19454      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19455      * <p>
19456      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19457      * and this call will return before the new data has been loaded. Perform any post-processing
19458      * in a callback function, or in a "load" event handler.</strong>
19459      * <p>
19460      * @param {Object} options An object containing properties which control loading options:<ul>
19461      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19462      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19463      * passed the following arguments:<ul>
19464      * <li>r : Roo.data.Record[]</li>
19465      * <li>options: Options object from the load call</li>
19466      * <li>success: Boolean success indicator</li></ul></li>
19467      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19468      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19469      * </ul>
19470      */
19471     load : function(options){
19472         options = options || {};
19473         if(this.fireEvent("beforeload", this, options) !== false){
19474             this.storeOptions(options);
19475             var p = Roo.apply(options.params || {}, this.baseParams);
19476             // if meta was not loaded from remote source.. try requesting it.
19477             if (!this.reader.metaFromRemote) {
19478                 p._requestMeta = 1;
19479             }
19480             if(this.sortInfo && this.remoteSort){
19481                 var pn = this.paramNames;
19482                 p[pn["sort"]] = this.sortInfo.field;
19483                 p[pn["dir"]] = this.sortInfo.direction;
19484             }
19485             if (this.multiSort) {
19486                 var pn = this.paramNames;
19487                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19488             }
19489             
19490             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19491         }
19492     },
19493
19494     /**
19495      * Reloads the Record cache from the configured Proxy using the configured Reader and
19496      * the options from the last load operation performed.
19497      * @param {Object} options (optional) An object containing properties which may override the options
19498      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19499      * the most recently used options are reused).
19500      */
19501     reload : function(options){
19502         this.load(Roo.applyIf(options||{}, this.lastOptions));
19503     },
19504
19505     // private
19506     // Called as a callback by the Reader during a load operation.
19507     loadRecords : function(o, options, success){
19508         if(!o || success === false){
19509             if(success !== false){
19510                 this.fireEvent("load", this, [], options);
19511             }
19512             if(options.callback){
19513                 options.callback.call(options.scope || this, [], options, false);
19514             }
19515             return;
19516         }
19517         // if data returned failure - throw an exception.
19518         if (o.success === false) {
19519             // show a message if no listener is registered.
19520             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19521                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19522             }
19523             // loadmask wil be hooked into this..
19524             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19525             return;
19526         }
19527         var r = o.records, t = o.totalRecords || r.length;
19528         if(!options || options.add !== true){
19529             if(this.pruneModifiedRecords){
19530                 this.modified = [];
19531             }
19532             for(var i = 0, len = r.length; i < len; i++){
19533                 r[i].join(this);
19534             }
19535             if(this.snapshot){
19536                 this.data = this.snapshot;
19537                 delete this.snapshot;
19538             }
19539             this.data.clear();
19540             this.data.addAll(r);
19541             this.totalLength = t;
19542             this.applySort();
19543             this.fireEvent("datachanged", this);
19544         }else{
19545             this.totalLength = Math.max(t, this.data.length+r.length);
19546             this.add(r);
19547         }
19548         this.fireEvent("load", this, r, options);
19549         if(options.callback){
19550             options.callback.call(options.scope || this, r, options, true);
19551         }
19552     },
19553
19554
19555     /**
19556      * Loads data from a passed data block. A Reader which understands the format of the data
19557      * must have been configured in the constructor.
19558      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19559      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19560      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19561      */
19562     loadData : function(o, append){
19563         var r = this.reader.readRecords(o);
19564         this.loadRecords(r, {add: append}, true);
19565     },
19566
19567     /**
19568      * Gets the number of cached records.
19569      * <p>
19570      * <em>If using paging, this may not be the total size of the dataset. If the data object
19571      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19572      * the data set size</em>
19573      */
19574     getCount : function(){
19575         return this.data.length || 0;
19576     },
19577
19578     /**
19579      * Gets the total number of records in the dataset as returned by the server.
19580      * <p>
19581      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19582      * the dataset size</em>
19583      */
19584     getTotalCount : function(){
19585         return this.totalLength || 0;
19586     },
19587
19588     /**
19589      * Returns the sort state of the Store as an object with two properties:
19590      * <pre><code>
19591  field {String} The name of the field by which the Records are sorted
19592  direction {String} The sort order, "ASC" or "DESC"
19593      * </code></pre>
19594      */
19595     getSortState : function(){
19596         return this.sortInfo;
19597     },
19598
19599     // private
19600     applySort : function(){
19601         if(this.sortInfo && !this.remoteSort){
19602             var s = this.sortInfo, f = s.field;
19603             var st = this.fields.get(f).sortType;
19604             var fn = function(r1, r2){
19605                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19606                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19607             };
19608             this.data.sort(s.direction, fn);
19609             if(this.snapshot && this.snapshot != this.data){
19610                 this.snapshot.sort(s.direction, fn);
19611             }
19612         }
19613     },
19614
19615     /**
19616      * Sets the default sort column and order to be used by the next load operation.
19617      * @param {String} fieldName The name of the field to sort by.
19618      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19619      */
19620     setDefaultSort : function(field, dir){
19621         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19622     },
19623
19624     /**
19625      * Sort the Records.
19626      * If remote sorting is used, the sort is performed on the server, and the cache is
19627      * reloaded. If local sorting is used, the cache is sorted internally.
19628      * @param {String} fieldName The name of the field to sort by.
19629      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19630      */
19631     sort : function(fieldName, dir){
19632         var f = this.fields.get(fieldName);
19633         if(!dir){
19634             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19635             
19636             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19637                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19638             }else{
19639                 dir = f.sortDir;
19640             }
19641         }
19642         this.sortToggle[f.name] = dir;
19643         this.sortInfo = {field: f.name, direction: dir};
19644         if(!this.remoteSort){
19645             this.applySort();
19646             this.fireEvent("datachanged", this);
19647         }else{
19648             this.load(this.lastOptions);
19649         }
19650     },
19651
19652     /**
19653      * Calls the specified function for each of the Records in the cache.
19654      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19655      * Returning <em>false</em> aborts and exits the iteration.
19656      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19657      */
19658     each : function(fn, scope){
19659         this.data.each(fn, scope);
19660     },
19661
19662     /**
19663      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19664      * (e.g., during paging).
19665      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19666      */
19667     getModifiedRecords : function(){
19668         return this.modified;
19669     },
19670
19671     // private
19672     createFilterFn : function(property, value, anyMatch){
19673         if(!value.exec){ // not a regex
19674             value = String(value);
19675             if(value.length == 0){
19676                 return false;
19677             }
19678             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19679         }
19680         return function(r){
19681             return value.test(r.data[property]);
19682         };
19683     },
19684
19685     /**
19686      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19687      * @param {String} property A field on your records
19688      * @param {Number} start The record index to start at (defaults to 0)
19689      * @param {Number} end The last record index to include (defaults to length - 1)
19690      * @return {Number} The sum
19691      */
19692     sum : function(property, start, end){
19693         var rs = this.data.items, v = 0;
19694         start = start || 0;
19695         end = (end || end === 0) ? end : rs.length-1;
19696
19697         for(var i = start; i <= end; i++){
19698             v += (rs[i].data[property] || 0);
19699         }
19700         return v;
19701     },
19702
19703     /**
19704      * Filter the records by a specified property.
19705      * @param {String} field A field on your records
19706      * @param {String/RegExp} value Either a string that the field
19707      * should start with or a RegExp to test against the field
19708      * @param {Boolean} anyMatch True to match any part not just the beginning
19709      */
19710     filter : function(property, value, anyMatch){
19711         var fn = this.createFilterFn(property, value, anyMatch);
19712         return fn ? this.filterBy(fn) : this.clearFilter();
19713     },
19714
19715     /**
19716      * Filter by a function. The specified function will be called with each
19717      * record in this data source. If the function returns true the record is included,
19718      * otherwise it is filtered.
19719      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19720      * @param {Object} scope (optional) The scope of the function (defaults to this)
19721      */
19722     filterBy : function(fn, scope){
19723         this.snapshot = this.snapshot || this.data;
19724         this.data = this.queryBy(fn, scope||this);
19725         this.fireEvent("datachanged", this);
19726     },
19727
19728     /**
19729      * Query the records by a specified property.
19730      * @param {String} field A field on your records
19731      * @param {String/RegExp} value Either a string that the field
19732      * should start with or a RegExp to test against the field
19733      * @param {Boolean} anyMatch True to match any part not just the beginning
19734      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19735      */
19736     query : function(property, value, anyMatch){
19737         var fn = this.createFilterFn(property, value, anyMatch);
19738         return fn ? this.queryBy(fn) : this.data.clone();
19739     },
19740
19741     /**
19742      * Query by a function. The specified function will be called with each
19743      * record in this data source. If the function returns true the record is included
19744      * in the results.
19745      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19746      * @param {Object} scope (optional) The scope of the function (defaults to this)
19747       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19748      **/
19749     queryBy : function(fn, scope){
19750         var data = this.snapshot || this.data;
19751         return data.filterBy(fn, scope||this);
19752     },
19753
19754     /**
19755      * Collects unique values for a particular dataIndex from this store.
19756      * @param {String} dataIndex The property to collect
19757      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19758      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19759      * @return {Array} An array of the unique values
19760      **/
19761     collect : function(dataIndex, allowNull, bypassFilter){
19762         var d = (bypassFilter === true && this.snapshot) ?
19763                 this.snapshot.items : this.data.items;
19764         var v, sv, r = [], l = {};
19765         for(var i = 0, len = d.length; i < len; i++){
19766             v = d[i].data[dataIndex];
19767             sv = String(v);
19768             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19769                 l[sv] = true;
19770                 r[r.length] = v;
19771             }
19772         }
19773         return r;
19774     },
19775
19776     /**
19777      * Revert to a view of the Record cache with no filtering applied.
19778      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19779      */
19780     clearFilter : function(suppressEvent){
19781         if(this.snapshot && this.snapshot != this.data){
19782             this.data = this.snapshot;
19783             delete this.snapshot;
19784             if(suppressEvent !== true){
19785                 this.fireEvent("datachanged", this);
19786             }
19787         }
19788     },
19789
19790     // private
19791     afterEdit : function(record){
19792         if(this.modified.indexOf(record) == -1){
19793             this.modified.push(record);
19794         }
19795         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19796     },
19797     
19798     // private
19799     afterReject : function(record){
19800         this.modified.remove(record);
19801         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19802     },
19803
19804     // private
19805     afterCommit : function(record){
19806         this.modified.remove(record);
19807         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19808     },
19809
19810     /**
19811      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19812      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19813      */
19814     commitChanges : function(){
19815         var m = this.modified.slice(0);
19816         this.modified = [];
19817         for(var i = 0, len = m.length; i < len; i++){
19818             m[i].commit();
19819         }
19820     },
19821
19822     /**
19823      * Cancel outstanding changes on all changed records.
19824      */
19825     rejectChanges : function(){
19826         var m = this.modified.slice(0);
19827         this.modified = [];
19828         for(var i = 0, len = m.length; i < len; i++){
19829             m[i].reject();
19830         }
19831     },
19832
19833     onMetaChange : function(meta, rtype, o){
19834         this.recordType = rtype;
19835         this.fields = rtype.prototype.fields;
19836         delete this.snapshot;
19837         this.sortInfo = meta.sortInfo || this.sortInfo;
19838         this.modified = [];
19839         this.fireEvent('metachange', this, this.reader.meta);
19840     }
19841 });/*
19842  * Based on:
19843  * Ext JS Library 1.1.1
19844  * Copyright(c) 2006-2007, Ext JS, LLC.
19845  *
19846  * Originally Released Under LGPL - original licence link has changed is not relivant.
19847  *
19848  * Fork - LGPL
19849  * <script type="text/javascript">
19850  */
19851
19852 /**
19853  * @class Roo.data.SimpleStore
19854  * @extends Roo.data.Store
19855  * Small helper class to make creating Stores from Array data easier.
19856  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19857  * @cfg {Array} fields An array of field definition objects, or field name strings.
19858  * @cfg {Array} data The multi-dimensional array of data
19859  * @constructor
19860  * @param {Object} config
19861  */
19862 Roo.data.SimpleStore = function(config){
19863     Roo.data.SimpleStore.superclass.constructor.call(this, {
19864         isLocal : true,
19865         reader: new Roo.data.ArrayReader({
19866                 id: config.id
19867             },
19868             Roo.data.Record.create(config.fields)
19869         ),
19870         proxy : new Roo.data.MemoryProxy(config.data)
19871     });
19872     this.load();
19873 };
19874 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19875  * Based on:
19876  * Ext JS Library 1.1.1
19877  * Copyright(c) 2006-2007, Ext JS, LLC.
19878  *
19879  * Originally Released Under LGPL - original licence link has changed is not relivant.
19880  *
19881  * Fork - LGPL
19882  * <script type="text/javascript">
19883  */
19884
19885 /**
19886 /**
19887  * @extends Roo.data.Store
19888  * @class Roo.data.JsonStore
19889  * Small helper class to make creating Stores for JSON data easier. <br/>
19890 <pre><code>
19891 var store = new Roo.data.JsonStore({
19892     url: 'get-images.php',
19893     root: 'images',
19894     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19895 });
19896 </code></pre>
19897  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19898  * JsonReader and HttpProxy (unless inline data is provided).</b>
19899  * @cfg {Array} fields An array of field definition objects, or field name strings.
19900  * @constructor
19901  * @param {Object} config
19902  */
19903 Roo.data.JsonStore = function(c){
19904     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19905         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19906         reader: new Roo.data.JsonReader(c, c.fields)
19907     }));
19908 };
19909 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19910  * Based on:
19911  * Ext JS Library 1.1.1
19912  * Copyright(c) 2006-2007, Ext JS, LLC.
19913  *
19914  * Originally Released Under LGPL - original licence link has changed is not relivant.
19915  *
19916  * Fork - LGPL
19917  * <script type="text/javascript">
19918  */
19919
19920  
19921 Roo.data.Field = function(config){
19922     if(typeof config == "string"){
19923         config = {name: config};
19924     }
19925     Roo.apply(this, config);
19926     
19927     if(!this.type){
19928         this.type = "auto";
19929     }
19930     
19931     var st = Roo.data.SortTypes;
19932     // named sortTypes are supported, here we look them up
19933     if(typeof this.sortType == "string"){
19934         this.sortType = st[this.sortType];
19935     }
19936     
19937     // set default sortType for strings and dates
19938     if(!this.sortType){
19939         switch(this.type){
19940             case "string":
19941                 this.sortType = st.asUCString;
19942                 break;
19943             case "date":
19944                 this.sortType = st.asDate;
19945                 break;
19946             default:
19947                 this.sortType = st.none;
19948         }
19949     }
19950
19951     // define once
19952     var stripRe = /[\$,%]/g;
19953
19954     // prebuilt conversion function for this field, instead of
19955     // switching every time we're reading a value
19956     if(!this.convert){
19957         var cv, dateFormat = this.dateFormat;
19958         switch(this.type){
19959             case "":
19960             case "auto":
19961             case undefined:
19962                 cv = function(v){ return v; };
19963                 break;
19964             case "string":
19965                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19966                 break;
19967             case "int":
19968                 cv = function(v){
19969                     return v !== undefined && v !== null && v !== '' ?
19970                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19971                     };
19972                 break;
19973             case "float":
19974                 cv = function(v){
19975                     return v !== undefined && v !== null && v !== '' ?
19976                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19977                     };
19978                 break;
19979             case "bool":
19980             case "boolean":
19981                 cv = function(v){ return v === true || v === "true" || v == 1; };
19982                 break;
19983             case "date":
19984                 cv = function(v){
19985                     if(!v){
19986                         return '';
19987                     }
19988                     if(v instanceof Date){
19989                         return v;
19990                     }
19991                     if(dateFormat){
19992                         if(dateFormat == "timestamp"){
19993                             return new Date(v*1000);
19994                         }
19995                         return Date.parseDate(v, dateFormat);
19996                     }
19997                     var parsed = Date.parse(v);
19998                     return parsed ? new Date(parsed) : null;
19999                 };
20000              break;
20001             
20002         }
20003         this.convert = cv;
20004     }
20005 };
20006
20007 Roo.data.Field.prototype = {
20008     dateFormat: null,
20009     defaultValue: "",
20010     mapping: null,
20011     sortType : null,
20012     sortDir : "ASC"
20013 };/*
20014  * Based on:
20015  * Ext JS Library 1.1.1
20016  * Copyright(c) 2006-2007, Ext JS, LLC.
20017  *
20018  * Originally Released Under LGPL - original licence link has changed is not relivant.
20019  *
20020  * Fork - LGPL
20021  * <script type="text/javascript">
20022  */
20023  
20024 // Base class for reading structured data from a data source.  This class is intended to be
20025 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20026
20027 /**
20028  * @class Roo.data.DataReader
20029  * Base class for reading structured data from a data source.  This class is intended to be
20030  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20031  */
20032
20033 Roo.data.DataReader = function(meta, recordType){
20034     
20035     this.meta = meta;
20036     
20037     this.recordType = recordType instanceof Array ? 
20038         Roo.data.Record.create(recordType) : recordType;
20039 };
20040
20041 Roo.data.DataReader.prototype = {
20042      /**
20043      * Create an empty record
20044      * @param {Object} data (optional) - overlay some values
20045      * @return {Roo.data.Record} record created.
20046      */
20047     newRow :  function(d) {
20048         var da =  {};
20049         this.recordType.prototype.fields.each(function(c) {
20050             switch( c.type) {
20051                 case 'int' : da[c.name] = 0; break;
20052                 case 'date' : da[c.name] = new Date(); break;
20053                 case 'float' : da[c.name] = 0.0; break;
20054                 case 'boolean' : da[c.name] = false; break;
20055                 default : da[c.name] = ""; break;
20056             }
20057             
20058         });
20059         return new this.recordType(Roo.apply(da, d));
20060     }
20061     
20062 };/*
20063  * Based on:
20064  * Ext JS Library 1.1.1
20065  * Copyright(c) 2006-2007, Ext JS, LLC.
20066  *
20067  * Originally Released Under LGPL - original licence link has changed is not relivant.
20068  *
20069  * Fork - LGPL
20070  * <script type="text/javascript">
20071  */
20072
20073 /**
20074  * @class Roo.data.DataProxy
20075  * @extends Roo.data.Observable
20076  * This class is an abstract base class for implementations which provide retrieval of
20077  * unformatted data objects.<br>
20078  * <p>
20079  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20080  * (of the appropriate type which knows how to parse the data object) to provide a block of
20081  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20082  * <p>
20083  * Custom implementations must implement the load method as described in
20084  * {@link Roo.data.HttpProxy#load}.
20085  */
20086 Roo.data.DataProxy = function(){
20087     this.addEvents({
20088         /**
20089          * @event beforeload
20090          * Fires before a network request is made to retrieve a data object.
20091          * @param {Object} This DataProxy object.
20092          * @param {Object} params The params parameter to the load function.
20093          */
20094         beforeload : true,
20095         /**
20096          * @event load
20097          * Fires before the load method's callback is called.
20098          * @param {Object} This DataProxy object.
20099          * @param {Object} o The data object.
20100          * @param {Object} arg The callback argument object passed to the load function.
20101          */
20102         load : true,
20103         /**
20104          * @event loadexception
20105          * Fires if an Exception occurs during data retrieval.
20106          * @param {Object} This DataProxy object.
20107          * @param {Object} o The data object.
20108          * @param {Object} arg The callback argument object passed to the load function.
20109          * @param {Object} e The Exception.
20110          */
20111         loadexception : true
20112     });
20113     Roo.data.DataProxy.superclass.constructor.call(this);
20114 };
20115
20116 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20117
20118     /**
20119      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20120      */
20121 /*
20122  * Based on:
20123  * Ext JS Library 1.1.1
20124  * Copyright(c) 2006-2007, Ext JS, LLC.
20125  *
20126  * Originally Released Under LGPL - original licence link has changed is not relivant.
20127  *
20128  * Fork - LGPL
20129  * <script type="text/javascript">
20130  */
20131 /**
20132  * @class Roo.data.MemoryProxy
20133  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20134  * to the Reader when its load method is called.
20135  * @constructor
20136  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20137  */
20138 Roo.data.MemoryProxy = function(data){
20139     if (data.data) {
20140         data = data.data;
20141     }
20142     Roo.data.MemoryProxy.superclass.constructor.call(this);
20143     this.data = data;
20144 };
20145
20146 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20147     /**
20148      * Load data from the requested source (in this case an in-memory
20149      * data object passed to the constructor), read the data object into
20150      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20151      * process that block using the passed callback.
20152      * @param {Object} params This parameter is not used by the MemoryProxy class.
20153      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20154      * object into a block of Roo.data.Records.
20155      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20156      * The function must be passed <ul>
20157      * <li>The Record block object</li>
20158      * <li>The "arg" argument from the load function</li>
20159      * <li>A boolean success indicator</li>
20160      * </ul>
20161      * @param {Object} scope The scope in which to call the callback
20162      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20163      */
20164     load : function(params, reader, callback, scope, arg){
20165         params = params || {};
20166         var result;
20167         try {
20168             result = reader.readRecords(this.data);
20169         }catch(e){
20170             this.fireEvent("loadexception", this, arg, null, e);
20171             callback.call(scope, null, arg, false);
20172             return;
20173         }
20174         callback.call(scope, result, arg, true);
20175     },
20176     
20177     // private
20178     update : function(params, records){
20179         
20180     }
20181 });/*
20182  * Based on:
20183  * Ext JS Library 1.1.1
20184  * Copyright(c) 2006-2007, Ext JS, LLC.
20185  *
20186  * Originally Released Under LGPL - original licence link has changed is not relivant.
20187  *
20188  * Fork - LGPL
20189  * <script type="text/javascript">
20190  */
20191 /**
20192  * @class Roo.data.HttpProxy
20193  * @extends Roo.data.DataProxy
20194  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20195  * configured to reference a certain URL.<br><br>
20196  * <p>
20197  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20198  * from which the running page was served.<br><br>
20199  * <p>
20200  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20201  * <p>
20202  * Be aware that to enable the browser to parse an XML document, the server must set
20203  * the Content-Type header in the HTTP response to "text/xml".
20204  * @constructor
20205  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20206  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20207  * will be used to make the request.
20208  */
20209 Roo.data.HttpProxy = function(conn){
20210     Roo.data.HttpProxy.superclass.constructor.call(this);
20211     // is conn a conn config or a real conn?
20212     this.conn = conn;
20213     this.useAjax = !conn || !conn.events;
20214   
20215 };
20216
20217 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20218     // thse are take from connection...
20219     
20220     /**
20221      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20222      */
20223     /**
20224      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20225      * extra parameters to each request made by this object. (defaults to undefined)
20226      */
20227     /**
20228      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20229      *  to each request made by this object. (defaults to undefined)
20230      */
20231     /**
20232      * @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)
20233      */
20234     /**
20235      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20236      */
20237      /**
20238      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20239      * @type Boolean
20240      */
20241   
20242
20243     /**
20244      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20245      * @type Boolean
20246      */
20247     /**
20248      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20249      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20250      * a finer-grained basis than the DataProxy events.
20251      */
20252     getConnection : function(){
20253         return this.useAjax ? Roo.Ajax : this.conn;
20254     },
20255
20256     /**
20257      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20258      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20259      * process that block using the passed callback.
20260      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20261      * for the request to the remote server.
20262      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20263      * object into a block of Roo.data.Records.
20264      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20265      * The function must be passed <ul>
20266      * <li>The Record block object</li>
20267      * <li>The "arg" argument from the load function</li>
20268      * <li>A boolean success indicator</li>
20269      * </ul>
20270      * @param {Object} scope The scope in which to call the callback
20271      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20272      */
20273     load : function(params, reader, callback, scope, arg){
20274         if(this.fireEvent("beforeload", this, params) !== false){
20275             var  o = {
20276                 params : params || {},
20277                 request: {
20278                     callback : callback,
20279                     scope : scope,
20280                     arg : arg
20281                 },
20282                 reader: reader,
20283                 callback : this.loadResponse,
20284                 scope: this
20285             };
20286             if(this.useAjax){
20287                 Roo.applyIf(o, this.conn);
20288                 if(this.activeRequest){
20289                     Roo.Ajax.abort(this.activeRequest);
20290                 }
20291                 this.activeRequest = Roo.Ajax.request(o);
20292             }else{
20293                 this.conn.request(o);
20294             }
20295         }else{
20296             callback.call(scope||this, null, arg, false);
20297         }
20298     },
20299
20300     // private
20301     loadResponse : function(o, success, response){
20302         delete this.activeRequest;
20303         if(!success){
20304             this.fireEvent("loadexception", this, o, response);
20305             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20306             return;
20307         }
20308         var result;
20309         try {
20310             result = o.reader.read(response);
20311         }catch(e){
20312             this.fireEvent("loadexception", this, o, response, e);
20313             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20314             return;
20315         }
20316         
20317         this.fireEvent("load", this, o, o.request.arg);
20318         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20319     },
20320
20321     // private
20322     update : function(dataSet){
20323
20324     },
20325
20326     // private
20327     updateResponse : function(dataSet){
20328
20329     }
20330 });/*
20331  * Based on:
20332  * Ext JS Library 1.1.1
20333  * Copyright(c) 2006-2007, Ext JS, LLC.
20334  *
20335  * Originally Released Under LGPL - original licence link has changed is not relivant.
20336  *
20337  * Fork - LGPL
20338  * <script type="text/javascript">
20339  */
20340
20341 /**
20342  * @class Roo.data.ScriptTagProxy
20343  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20344  * other than the originating domain of the running page.<br><br>
20345  * <p>
20346  * <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
20347  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20348  * <p>
20349  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20350  * source code that is used as the source inside a &lt;script> tag.<br><br>
20351  * <p>
20352  * In order for the browser to process the returned data, the server must wrap the data object
20353  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20354  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20355  * depending on whether the callback name was passed:
20356  * <p>
20357  * <pre><code>
20358 boolean scriptTag = false;
20359 String cb = request.getParameter("callback");
20360 if (cb != null) {
20361     scriptTag = true;
20362     response.setContentType("text/javascript");
20363 } else {
20364     response.setContentType("application/x-json");
20365 }
20366 Writer out = response.getWriter();
20367 if (scriptTag) {
20368     out.write(cb + "(");
20369 }
20370 out.print(dataBlock.toJsonString());
20371 if (scriptTag) {
20372     out.write(");");
20373 }
20374 </pre></code>
20375  *
20376  * @constructor
20377  * @param {Object} config A configuration object.
20378  */
20379 Roo.data.ScriptTagProxy = function(config){
20380     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20381     Roo.apply(this, config);
20382     this.head = document.getElementsByTagName("head")[0];
20383 };
20384
20385 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20386
20387 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20388     /**
20389      * @cfg {String} url The URL from which to request the data object.
20390      */
20391     /**
20392      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20393      */
20394     timeout : 30000,
20395     /**
20396      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20397      * the server the name of the callback function set up by the load call to process the returned data object.
20398      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20399      * javascript output which calls this named function passing the data object as its only parameter.
20400      */
20401     callbackParam : "callback",
20402     /**
20403      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20404      * name to the request.
20405      */
20406     nocache : true,
20407
20408     /**
20409      * Load data from the configured URL, read the data object into
20410      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20411      * process that block using the passed callback.
20412      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20413      * for the request to the remote server.
20414      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20415      * object into a block of Roo.data.Records.
20416      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20417      * The function must be passed <ul>
20418      * <li>The Record block object</li>
20419      * <li>The "arg" argument from the load function</li>
20420      * <li>A boolean success indicator</li>
20421      * </ul>
20422      * @param {Object} scope The scope in which to call the callback
20423      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20424      */
20425     load : function(params, reader, callback, scope, arg){
20426         if(this.fireEvent("beforeload", this, params) !== false){
20427
20428             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20429
20430             var url = this.url;
20431             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20432             if(this.nocache){
20433                 url += "&_dc=" + (new Date().getTime());
20434             }
20435             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20436             var trans = {
20437                 id : transId,
20438                 cb : "stcCallback"+transId,
20439                 scriptId : "stcScript"+transId,
20440                 params : params,
20441                 arg : arg,
20442                 url : url,
20443                 callback : callback,
20444                 scope : scope,
20445                 reader : reader
20446             };
20447             var conn = this;
20448
20449             window[trans.cb] = function(o){
20450                 conn.handleResponse(o, trans);
20451             };
20452
20453             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20454
20455             if(this.autoAbort !== false){
20456                 this.abort();
20457             }
20458
20459             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20460
20461             var script = document.createElement("script");
20462             script.setAttribute("src", url);
20463             script.setAttribute("type", "text/javascript");
20464             script.setAttribute("id", trans.scriptId);
20465             this.head.appendChild(script);
20466
20467             this.trans = trans;
20468         }else{
20469             callback.call(scope||this, null, arg, false);
20470         }
20471     },
20472
20473     // private
20474     isLoading : function(){
20475         return this.trans ? true : false;
20476     },
20477
20478     /**
20479      * Abort the current server request.
20480      */
20481     abort : function(){
20482         if(this.isLoading()){
20483             this.destroyTrans(this.trans);
20484         }
20485     },
20486
20487     // private
20488     destroyTrans : function(trans, isLoaded){
20489         this.head.removeChild(document.getElementById(trans.scriptId));
20490         clearTimeout(trans.timeoutId);
20491         if(isLoaded){
20492             window[trans.cb] = undefined;
20493             try{
20494                 delete window[trans.cb];
20495             }catch(e){}
20496         }else{
20497             // if hasn't been loaded, wait for load to remove it to prevent script error
20498             window[trans.cb] = function(){
20499                 window[trans.cb] = undefined;
20500                 try{
20501                     delete window[trans.cb];
20502                 }catch(e){}
20503             };
20504         }
20505     },
20506
20507     // private
20508     handleResponse : function(o, trans){
20509         this.trans = false;
20510         this.destroyTrans(trans, true);
20511         var result;
20512         try {
20513             result = trans.reader.readRecords(o);
20514         }catch(e){
20515             this.fireEvent("loadexception", this, o, trans.arg, e);
20516             trans.callback.call(trans.scope||window, null, trans.arg, false);
20517             return;
20518         }
20519         this.fireEvent("load", this, o, trans.arg);
20520         trans.callback.call(trans.scope||window, result, trans.arg, true);
20521     },
20522
20523     // private
20524     handleFailure : function(trans){
20525         this.trans = false;
20526         this.destroyTrans(trans, false);
20527         this.fireEvent("loadexception", this, null, trans.arg);
20528         trans.callback.call(trans.scope||window, null, trans.arg, false);
20529     }
20530 });/*
20531  * Based on:
20532  * Ext JS Library 1.1.1
20533  * Copyright(c) 2006-2007, Ext JS, LLC.
20534  *
20535  * Originally Released Under LGPL - original licence link has changed is not relivant.
20536  *
20537  * Fork - LGPL
20538  * <script type="text/javascript">
20539  */
20540
20541 /**
20542  * @class Roo.data.JsonReader
20543  * @extends Roo.data.DataReader
20544  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20545  * based on mappings in a provided Roo.data.Record constructor.
20546  * 
20547  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20548  * in the reply previously. 
20549  * 
20550  * <p>
20551  * Example code:
20552  * <pre><code>
20553 var RecordDef = Roo.data.Record.create([
20554     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20555     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20556 ]);
20557 var myReader = new Roo.data.JsonReader({
20558     totalProperty: "results",    // The property which contains the total dataset size (optional)
20559     root: "rows",                // The property which contains an Array of row objects
20560     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20561 }, RecordDef);
20562 </code></pre>
20563  * <p>
20564  * This would consume a JSON file like this:
20565  * <pre><code>
20566 { 'results': 2, 'rows': [
20567     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20568     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20569 }
20570 </code></pre>
20571  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20572  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20573  * paged from the remote server.
20574  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20575  * @cfg {String} root name of the property which contains the Array of row objects.
20576  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20577  * @constructor
20578  * Create a new JsonReader
20579  * @param {Object} meta Metadata configuration options
20580  * @param {Object} recordType Either an Array of field definition objects,
20581  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20582  */
20583 Roo.data.JsonReader = function(meta, recordType){
20584     
20585     meta = meta || {};
20586     // set some defaults:
20587     Roo.applyIf(meta, {
20588         totalProperty: 'total',
20589         successProperty : 'success',
20590         root : 'data',
20591         id : 'id'
20592     });
20593     
20594     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20595 };
20596 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20597     
20598     /**
20599      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20600      * Used by Store query builder to append _requestMeta to params.
20601      * 
20602      */
20603     metaFromRemote : false,
20604     /**
20605      * This method is only used by a DataProxy which has retrieved data from a remote server.
20606      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20607      * @return {Object} data A data block which is used by an Roo.data.Store object as
20608      * a cache of Roo.data.Records.
20609      */
20610     read : function(response){
20611         var json = response.responseText;
20612        
20613         var o = /* eval:var:o */ eval("("+json+")");
20614         if(!o) {
20615             throw {message: "JsonReader.read: Json object not found"};
20616         }
20617         
20618         if(o.metaData){
20619             
20620             delete this.ef;
20621             this.metaFromRemote = true;
20622             this.meta = o.metaData;
20623             this.recordType = Roo.data.Record.create(o.metaData.fields);
20624             this.onMetaChange(this.meta, this.recordType, o);
20625         }
20626         return this.readRecords(o);
20627     },
20628
20629     // private function a store will implement
20630     onMetaChange : function(meta, recordType, o){
20631
20632     },
20633
20634     /**
20635          * @ignore
20636          */
20637     simpleAccess: function(obj, subsc) {
20638         return obj[subsc];
20639     },
20640
20641         /**
20642          * @ignore
20643          */
20644     getJsonAccessor: function(){
20645         var re = /[\[\.]/;
20646         return function(expr) {
20647             try {
20648                 return(re.test(expr))
20649                     ? new Function("obj", "return obj." + expr)
20650                     : function(obj){
20651                         return obj[expr];
20652                     };
20653             } catch(e){}
20654             return Roo.emptyFn;
20655         };
20656     }(),
20657
20658     /**
20659      * Create a data block containing Roo.data.Records from an XML document.
20660      * @param {Object} o An object which contains an Array of row objects in the property specified
20661      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20662      * which contains the total size of the dataset.
20663      * @return {Object} data A data block which is used by an Roo.data.Store object as
20664      * a cache of Roo.data.Records.
20665      */
20666     readRecords : function(o){
20667         /**
20668          * After any data loads, the raw JSON data is available for further custom processing.
20669          * @type Object
20670          */
20671         this.jsonData = o;
20672         var s = this.meta, Record = this.recordType,
20673             f = Record.prototype.fields, fi = f.items, fl = f.length;
20674
20675 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20676         if (!this.ef) {
20677             if(s.totalProperty) {
20678                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20679                 }
20680                 if(s.successProperty) {
20681                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20682                 }
20683                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20684                 if (s.id) {
20685                         var g = this.getJsonAccessor(s.id);
20686                         this.getId = function(rec) {
20687                                 var r = g(rec);
20688                                 return (r === undefined || r === "") ? null : r;
20689                         };
20690                 } else {
20691                         this.getId = function(){return null;};
20692                 }
20693             this.ef = [];
20694             for(var jj = 0; jj < fl; jj++){
20695                 f = fi[jj];
20696                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20697                 this.ef[jj] = this.getJsonAccessor(map);
20698             }
20699         }
20700
20701         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20702         if(s.totalProperty){
20703             var vt = parseInt(this.getTotal(o), 10);
20704             if(!isNaN(vt)){
20705                 totalRecords = vt;
20706             }
20707         }
20708         if(s.successProperty){
20709             var vs = this.getSuccess(o);
20710             if(vs === false || vs === 'false'){
20711                 success = false;
20712             }
20713         }
20714         var records = [];
20715             for(var i = 0; i < c; i++){
20716                     var n = root[i];
20717                 var values = {};
20718                 var id = this.getId(n);
20719                 for(var j = 0; j < fl; j++){
20720                     f = fi[j];
20721                 var v = this.ef[j](n);
20722                 if (!f.convert) {
20723                     Roo.log('missing convert for ' + f.name);
20724                     Roo.log(f);
20725                     continue;
20726                 }
20727                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20728                 }
20729                 var record = new Record(values, id);
20730                 record.json = n;
20731                 records[i] = record;
20732             }
20733             return {
20734                 success : success,
20735                 records : records,
20736                 totalRecords : totalRecords
20737             };
20738     }
20739 });/*
20740  * Based on:
20741  * Ext JS Library 1.1.1
20742  * Copyright(c) 2006-2007, Ext JS, LLC.
20743  *
20744  * Originally Released Under LGPL - original licence link has changed is not relivant.
20745  *
20746  * Fork - LGPL
20747  * <script type="text/javascript">
20748  */
20749
20750 /**
20751  * @class Roo.data.XmlReader
20752  * @extends Roo.data.DataReader
20753  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20754  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20755  * <p>
20756  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20757  * header in the HTTP response must be set to "text/xml".</em>
20758  * <p>
20759  * Example code:
20760  * <pre><code>
20761 var RecordDef = Roo.data.Record.create([
20762    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20763    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20764 ]);
20765 var myReader = new Roo.data.XmlReader({
20766    totalRecords: "results", // The element which contains the total dataset size (optional)
20767    record: "row",           // The repeated element which contains row information
20768    id: "id"                 // The element within the row that provides an ID for the record (optional)
20769 }, RecordDef);
20770 </code></pre>
20771  * <p>
20772  * This would consume an XML file like this:
20773  * <pre><code>
20774 &lt;?xml?>
20775 &lt;dataset>
20776  &lt;results>2&lt;/results>
20777  &lt;row>
20778    &lt;id>1&lt;/id>
20779    &lt;name>Bill&lt;/name>
20780    &lt;occupation>Gardener&lt;/occupation>
20781  &lt;/row>
20782  &lt;row>
20783    &lt;id>2&lt;/id>
20784    &lt;name>Ben&lt;/name>
20785    &lt;occupation>Horticulturalist&lt;/occupation>
20786  &lt;/row>
20787 &lt;/dataset>
20788 </code></pre>
20789  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20790  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20791  * paged from the remote server.
20792  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20793  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20794  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20795  * a record identifier value.
20796  * @constructor
20797  * Create a new XmlReader
20798  * @param {Object} meta Metadata configuration options
20799  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20800  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20801  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20802  */
20803 Roo.data.XmlReader = function(meta, recordType){
20804     meta = meta || {};
20805     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20806 };
20807 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20808     /**
20809      * This method is only used by a DataProxy which has retrieved data from a remote server.
20810          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20811          * to contain a method called 'responseXML' that returns an XML document object.
20812      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20813      * a cache of Roo.data.Records.
20814      */
20815     read : function(response){
20816         var doc = response.responseXML;
20817         if(!doc) {
20818             throw {message: "XmlReader.read: XML Document not available"};
20819         }
20820         return this.readRecords(doc);
20821     },
20822
20823     /**
20824      * Create a data block containing Roo.data.Records from an XML document.
20825          * @param {Object} doc A parsed XML document.
20826      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20827      * a cache of Roo.data.Records.
20828      */
20829     readRecords : function(doc){
20830         /**
20831          * After any data loads/reads, the raw XML Document is available for further custom processing.
20832          * @type XMLDocument
20833          */
20834         this.xmlData = doc;
20835         var root = doc.documentElement || doc;
20836         var q = Roo.DomQuery;
20837         var recordType = this.recordType, fields = recordType.prototype.fields;
20838         var sid = this.meta.id;
20839         var totalRecords = 0, success = true;
20840         if(this.meta.totalRecords){
20841             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20842         }
20843         
20844         if(this.meta.success){
20845             var sv = q.selectValue(this.meta.success, root, true);
20846             success = sv !== false && sv !== 'false';
20847         }
20848         var records = [];
20849         var ns = q.select(this.meta.record, root);
20850         for(var i = 0, len = ns.length; i < len; i++) {
20851                 var n = ns[i];
20852                 var values = {};
20853                 var id = sid ? q.selectValue(sid, n) : undefined;
20854                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20855                     var f = fields.items[j];
20856                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20857                     v = f.convert(v);
20858                     values[f.name] = v;
20859                 }
20860                 var record = new recordType(values, id);
20861                 record.node = n;
20862                 records[records.length] = record;
20863             }
20864
20865             return {
20866                 success : success,
20867                 records : records,
20868                 totalRecords : totalRecords || records.length
20869             };
20870     }
20871 });/*
20872  * Based on:
20873  * Ext JS Library 1.1.1
20874  * Copyright(c) 2006-2007, Ext JS, LLC.
20875  *
20876  * Originally Released Under LGPL - original licence link has changed is not relivant.
20877  *
20878  * Fork - LGPL
20879  * <script type="text/javascript">
20880  */
20881
20882 /**
20883  * @class Roo.data.ArrayReader
20884  * @extends Roo.data.DataReader
20885  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20886  * Each element of that Array represents a row of data fields. The
20887  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20888  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20889  * <p>
20890  * Example code:.
20891  * <pre><code>
20892 var RecordDef = Roo.data.Record.create([
20893     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20894     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20895 ]);
20896 var myReader = new Roo.data.ArrayReader({
20897     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20898 }, RecordDef);
20899 </code></pre>
20900  * <p>
20901  * This would consume an Array like this:
20902  * <pre><code>
20903 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20904   </code></pre>
20905  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20906  * @constructor
20907  * Create a new JsonReader
20908  * @param {Object} meta Metadata configuration options.
20909  * @param {Object} recordType Either an Array of field definition objects
20910  * as specified to {@link Roo.data.Record#create},
20911  * or an {@link Roo.data.Record} object
20912  * created using {@link Roo.data.Record#create}.
20913  */
20914 Roo.data.ArrayReader = function(meta, recordType){
20915     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20916 };
20917
20918 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20919     /**
20920      * Create a data block containing Roo.data.Records from an XML document.
20921      * @param {Object} o An Array of row objects which represents the dataset.
20922      * @return {Object} data A data block which is used by an Roo.data.Store object as
20923      * a cache of Roo.data.Records.
20924      */
20925     readRecords : function(o){
20926         var sid = this.meta ? this.meta.id : null;
20927         var recordType = this.recordType, fields = recordType.prototype.fields;
20928         var records = [];
20929         var root = o;
20930             for(var i = 0; i < root.length; i++){
20931                     var n = root[i];
20932                 var values = {};
20933                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20934                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20935                 var f = fields.items[j];
20936                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20937                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20938                 v = f.convert(v);
20939                 values[f.name] = v;
20940             }
20941                 var record = new recordType(values, id);
20942                 record.json = n;
20943                 records[records.length] = record;
20944             }
20945             return {
20946                 records : records,
20947                 totalRecords : records.length
20948             };
20949     }
20950 });/*
20951  * Based on:
20952  * Ext JS Library 1.1.1
20953  * Copyright(c) 2006-2007, Ext JS, LLC.
20954  *
20955  * Originally Released Under LGPL - original licence link has changed is not relivant.
20956  *
20957  * Fork - LGPL
20958  * <script type="text/javascript">
20959  */
20960
20961
20962 /**
20963  * @class Roo.data.Tree
20964  * @extends Roo.util.Observable
20965  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20966  * in the tree have most standard DOM functionality.
20967  * @constructor
20968  * @param {Node} root (optional) The root node
20969  */
20970 Roo.data.Tree = function(root){
20971    this.nodeHash = {};
20972    /**
20973     * The root node for this tree
20974     * @type Node
20975     */
20976    this.root = null;
20977    if(root){
20978        this.setRootNode(root);
20979    }
20980    this.addEvents({
20981        /**
20982         * @event append
20983         * Fires when a new child node is appended to a node in this tree.
20984         * @param {Tree} tree The owner tree
20985         * @param {Node} parent The parent node
20986         * @param {Node} node The newly appended node
20987         * @param {Number} index The index of the newly appended node
20988         */
20989        "append" : true,
20990        /**
20991         * @event remove
20992         * Fires when a child node is removed from a node in this tree.
20993         * @param {Tree} tree The owner tree
20994         * @param {Node} parent The parent node
20995         * @param {Node} node The child node removed
20996         */
20997        "remove" : true,
20998        /**
20999         * @event move
21000         * Fires when a node is moved to a new location in the tree
21001         * @param {Tree} tree The owner tree
21002         * @param {Node} node The node moved
21003         * @param {Node} oldParent The old parent of this node
21004         * @param {Node} newParent The new parent of this node
21005         * @param {Number} index The index it was moved to
21006         */
21007        "move" : true,
21008        /**
21009         * @event insert
21010         * Fires when a new child node is inserted in a node in this tree.
21011         * @param {Tree} tree The owner tree
21012         * @param {Node} parent The parent node
21013         * @param {Node} node The child node inserted
21014         * @param {Node} refNode The child node the node was inserted before
21015         */
21016        "insert" : true,
21017        /**
21018         * @event beforeappend
21019         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21020         * @param {Tree} tree The owner tree
21021         * @param {Node} parent The parent node
21022         * @param {Node} node The child node to be appended
21023         */
21024        "beforeappend" : true,
21025        /**
21026         * @event beforeremove
21027         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21028         * @param {Tree} tree The owner tree
21029         * @param {Node} parent The parent node
21030         * @param {Node} node The child node to be removed
21031         */
21032        "beforeremove" : true,
21033        /**
21034         * @event beforemove
21035         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21036         * @param {Tree} tree The owner tree
21037         * @param {Node} node The node being moved
21038         * @param {Node} oldParent The parent of the node
21039         * @param {Node} newParent The new parent the node is moving to
21040         * @param {Number} index The index it is being moved to
21041         */
21042        "beforemove" : true,
21043        /**
21044         * @event beforeinsert
21045         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21046         * @param {Tree} tree The owner tree
21047         * @param {Node} parent The parent node
21048         * @param {Node} node The child node to be inserted
21049         * @param {Node} refNode The child node the node is being inserted before
21050         */
21051        "beforeinsert" : true
21052    });
21053
21054     Roo.data.Tree.superclass.constructor.call(this);
21055 };
21056
21057 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21058     pathSeparator: "/",
21059
21060     proxyNodeEvent : function(){
21061         return this.fireEvent.apply(this, arguments);
21062     },
21063
21064     /**
21065      * Returns the root node for this tree.
21066      * @return {Node}
21067      */
21068     getRootNode : function(){
21069         return this.root;
21070     },
21071
21072     /**
21073      * Sets the root node for this tree.
21074      * @param {Node} node
21075      * @return {Node}
21076      */
21077     setRootNode : function(node){
21078         this.root = node;
21079         node.ownerTree = this;
21080         node.isRoot = true;
21081         this.registerNode(node);
21082         return node;
21083     },
21084
21085     /**
21086      * Gets a node in this tree by its id.
21087      * @param {String} id
21088      * @return {Node}
21089      */
21090     getNodeById : function(id){
21091         return this.nodeHash[id];
21092     },
21093
21094     registerNode : function(node){
21095         this.nodeHash[node.id] = node;
21096     },
21097
21098     unregisterNode : function(node){
21099         delete this.nodeHash[node.id];
21100     },
21101
21102     toString : function(){
21103         return "[Tree"+(this.id?" "+this.id:"")+"]";
21104     }
21105 });
21106
21107 /**
21108  * @class Roo.data.Node
21109  * @extends Roo.util.Observable
21110  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21111  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21112  * @constructor
21113  * @param {Object} attributes The attributes/config for the node
21114  */
21115 Roo.data.Node = function(attributes){
21116     /**
21117      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21118      * @type {Object}
21119      */
21120     this.attributes = attributes || {};
21121     this.leaf = this.attributes.leaf;
21122     /**
21123      * The node id. @type String
21124      */
21125     this.id = this.attributes.id;
21126     if(!this.id){
21127         this.id = Roo.id(null, "ynode-");
21128         this.attributes.id = this.id;
21129     }
21130     /**
21131      * All child nodes of this node. @type Array
21132      */
21133     this.childNodes = [];
21134     if(!this.childNodes.indexOf){ // indexOf is a must
21135         this.childNodes.indexOf = function(o){
21136             for(var i = 0, len = this.length; i < len; i++){
21137                 if(this[i] == o) {
21138                     return i;
21139                 }
21140             }
21141             return -1;
21142         };
21143     }
21144     /**
21145      * The parent node for this node. @type Node
21146      */
21147     this.parentNode = null;
21148     /**
21149      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21150      */
21151     this.firstChild = null;
21152     /**
21153      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21154      */
21155     this.lastChild = null;
21156     /**
21157      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21158      */
21159     this.previousSibling = null;
21160     /**
21161      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21162      */
21163     this.nextSibling = null;
21164
21165     this.addEvents({
21166        /**
21167         * @event append
21168         * Fires when a new child node is appended
21169         * @param {Tree} tree The owner tree
21170         * @param {Node} this This node
21171         * @param {Node} node The newly appended node
21172         * @param {Number} index The index of the newly appended node
21173         */
21174        "append" : true,
21175        /**
21176         * @event remove
21177         * Fires when a child node is removed
21178         * @param {Tree} tree The owner tree
21179         * @param {Node} this This node
21180         * @param {Node} node The removed node
21181         */
21182        "remove" : true,
21183        /**
21184         * @event move
21185         * Fires when this node is moved to a new location in the tree
21186         * @param {Tree} tree The owner tree
21187         * @param {Node} this This node
21188         * @param {Node} oldParent The old parent of this node
21189         * @param {Node} newParent The new parent of this node
21190         * @param {Number} index The index it was moved to
21191         */
21192        "move" : true,
21193        /**
21194         * @event insert
21195         * Fires when a new child node is inserted.
21196         * @param {Tree} tree The owner tree
21197         * @param {Node} this This node
21198         * @param {Node} node The child node inserted
21199         * @param {Node} refNode The child node the node was inserted before
21200         */
21201        "insert" : true,
21202        /**
21203         * @event beforeappend
21204         * Fires before a new child is appended, return false to cancel the append.
21205         * @param {Tree} tree The owner tree
21206         * @param {Node} this This node
21207         * @param {Node} node The child node to be appended
21208         */
21209        "beforeappend" : true,
21210        /**
21211         * @event beforeremove
21212         * Fires before a child is removed, return false to cancel the remove.
21213         * @param {Tree} tree The owner tree
21214         * @param {Node} this This node
21215         * @param {Node} node The child node to be removed
21216         */
21217        "beforeremove" : true,
21218        /**
21219         * @event beforemove
21220         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21221         * @param {Tree} tree The owner tree
21222         * @param {Node} this This node
21223         * @param {Node} oldParent The parent of this node
21224         * @param {Node} newParent The new parent this node is moving to
21225         * @param {Number} index The index it is being moved to
21226         */
21227        "beforemove" : true,
21228        /**
21229         * @event beforeinsert
21230         * Fires before a new child is inserted, return false to cancel the insert.
21231         * @param {Tree} tree The owner tree
21232         * @param {Node} this This node
21233         * @param {Node} node The child node to be inserted
21234         * @param {Node} refNode The child node the node is being inserted before
21235         */
21236        "beforeinsert" : true
21237    });
21238     this.listeners = this.attributes.listeners;
21239     Roo.data.Node.superclass.constructor.call(this);
21240 };
21241
21242 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21243     fireEvent : function(evtName){
21244         // first do standard event for this node
21245         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21246             return false;
21247         }
21248         // then bubble it up to the tree if the event wasn't cancelled
21249         var ot = this.getOwnerTree();
21250         if(ot){
21251             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21252                 return false;
21253             }
21254         }
21255         return true;
21256     },
21257
21258     /**
21259      * Returns true if this node is a leaf
21260      * @return {Boolean}
21261      */
21262     isLeaf : function(){
21263         return this.leaf === true;
21264     },
21265
21266     // private
21267     setFirstChild : function(node){
21268         this.firstChild = node;
21269     },
21270
21271     //private
21272     setLastChild : function(node){
21273         this.lastChild = node;
21274     },
21275
21276
21277     /**
21278      * Returns true if this node is the last child of its parent
21279      * @return {Boolean}
21280      */
21281     isLast : function(){
21282        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21283     },
21284
21285     /**
21286      * Returns true if this node is the first child of its parent
21287      * @return {Boolean}
21288      */
21289     isFirst : function(){
21290        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21291     },
21292
21293     hasChildNodes : function(){
21294         return !this.isLeaf() && this.childNodes.length > 0;
21295     },
21296
21297     /**
21298      * Insert node(s) as the last child node of this node.
21299      * @param {Node/Array} node The node or Array of nodes to append
21300      * @return {Node} The appended node if single append, or null if an array was passed
21301      */
21302     appendChild : function(node){
21303         var multi = false;
21304         if(node instanceof Array){
21305             multi = node;
21306         }else if(arguments.length > 1){
21307             multi = arguments;
21308         }
21309         // if passed an array or multiple args do them one by one
21310         if(multi){
21311             for(var i = 0, len = multi.length; i < len; i++) {
21312                 this.appendChild(multi[i]);
21313             }
21314         }else{
21315             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21316                 return false;
21317             }
21318             var index = this.childNodes.length;
21319             var oldParent = node.parentNode;
21320             // it's a move, make sure we move it cleanly
21321             if(oldParent){
21322                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21323                     return false;
21324                 }
21325                 oldParent.removeChild(node);
21326             }
21327             index = this.childNodes.length;
21328             if(index == 0){
21329                 this.setFirstChild(node);
21330             }
21331             this.childNodes.push(node);
21332             node.parentNode = this;
21333             var ps = this.childNodes[index-1];
21334             if(ps){
21335                 node.previousSibling = ps;
21336                 ps.nextSibling = node;
21337             }else{
21338                 node.previousSibling = null;
21339             }
21340             node.nextSibling = null;
21341             this.setLastChild(node);
21342             node.setOwnerTree(this.getOwnerTree());
21343             this.fireEvent("append", this.ownerTree, this, node, index);
21344             if(oldParent){
21345                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21346             }
21347             return node;
21348         }
21349     },
21350
21351     /**
21352      * Removes a child node from this node.
21353      * @param {Node} node The node to remove
21354      * @return {Node} The removed node
21355      */
21356     removeChild : function(node){
21357         var index = this.childNodes.indexOf(node);
21358         if(index == -1){
21359             return false;
21360         }
21361         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21362             return false;
21363         }
21364
21365         // remove it from childNodes collection
21366         this.childNodes.splice(index, 1);
21367
21368         // update siblings
21369         if(node.previousSibling){
21370             node.previousSibling.nextSibling = node.nextSibling;
21371         }
21372         if(node.nextSibling){
21373             node.nextSibling.previousSibling = node.previousSibling;
21374         }
21375
21376         // update child refs
21377         if(this.firstChild == node){
21378             this.setFirstChild(node.nextSibling);
21379         }
21380         if(this.lastChild == node){
21381             this.setLastChild(node.previousSibling);
21382         }
21383
21384         node.setOwnerTree(null);
21385         // clear any references from the node
21386         node.parentNode = null;
21387         node.previousSibling = null;
21388         node.nextSibling = null;
21389         this.fireEvent("remove", this.ownerTree, this, node);
21390         return node;
21391     },
21392
21393     /**
21394      * Inserts the first node before the second node in this nodes childNodes collection.
21395      * @param {Node} node The node to insert
21396      * @param {Node} refNode The node to insert before (if null the node is appended)
21397      * @return {Node} The inserted node
21398      */
21399     insertBefore : function(node, refNode){
21400         if(!refNode){ // like standard Dom, refNode can be null for append
21401             return this.appendChild(node);
21402         }
21403         // nothing to do
21404         if(node == refNode){
21405             return false;
21406         }
21407
21408         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21409             return false;
21410         }
21411         var index = this.childNodes.indexOf(refNode);
21412         var oldParent = node.parentNode;
21413         var refIndex = index;
21414
21415         // when moving internally, indexes will change after remove
21416         if(oldParent == this && this.childNodes.indexOf(node) < index){
21417             refIndex--;
21418         }
21419
21420         // it's a move, make sure we move it cleanly
21421         if(oldParent){
21422             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21423                 return false;
21424             }
21425             oldParent.removeChild(node);
21426         }
21427         if(refIndex == 0){
21428             this.setFirstChild(node);
21429         }
21430         this.childNodes.splice(refIndex, 0, node);
21431         node.parentNode = this;
21432         var ps = this.childNodes[refIndex-1];
21433         if(ps){
21434             node.previousSibling = ps;
21435             ps.nextSibling = node;
21436         }else{
21437             node.previousSibling = null;
21438         }
21439         node.nextSibling = refNode;
21440         refNode.previousSibling = node;
21441         node.setOwnerTree(this.getOwnerTree());
21442         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21443         if(oldParent){
21444             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21445         }
21446         return node;
21447     },
21448
21449     /**
21450      * Returns the child node at the specified index.
21451      * @param {Number} index
21452      * @return {Node}
21453      */
21454     item : function(index){
21455         return this.childNodes[index];
21456     },
21457
21458     /**
21459      * Replaces one child node in this node with another.
21460      * @param {Node} newChild The replacement node
21461      * @param {Node} oldChild The node to replace
21462      * @return {Node} The replaced node
21463      */
21464     replaceChild : function(newChild, oldChild){
21465         this.insertBefore(newChild, oldChild);
21466         this.removeChild(oldChild);
21467         return oldChild;
21468     },
21469
21470     /**
21471      * Returns the index of a child node
21472      * @param {Node} node
21473      * @return {Number} The index of the node or -1 if it was not found
21474      */
21475     indexOf : function(child){
21476         return this.childNodes.indexOf(child);
21477     },
21478
21479     /**
21480      * Returns the tree this node is in.
21481      * @return {Tree}
21482      */
21483     getOwnerTree : function(){
21484         // if it doesn't have one, look for one
21485         if(!this.ownerTree){
21486             var p = this;
21487             while(p){
21488                 if(p.ownerTree){
21489                     this.ownerTree = p.ownerTree;
21490                     break;
21491                 }
21492                 p = p.parentNode;
21493             }
21494         }
21495         return this.ownerTree;
21496     },
21497
21498     /**
21499      * Returns depth of this node (the root node has a depth of 0)
21500      * @return {Number}
21501      */
21502     getDepth : function(){
21503         var depth = 0;
21504         var p = this;
21505         while(p.parentNode){
21506             ++depth;
21507             p = p.parentNode;
21508         }
21509         return depth;
21510     },
21511
21512     // private
21513     setOwnerTree : function(tree){
21514         // if it's move, we need to update everyone
21515         if(tree != this.ownerTree){
21516             if(this.ownerTree){
21517                 this.ownerTree.unregisterNode(this);
21518             }
21519             this.ownerTree = tree;
21520             var cs = this.childNodes;
21521             for(var i = 0, len = cs.length; i < len; i++) {
21522                 cs[i].setOwnerTree(tree);
21523             }
21524             if(tree){
21525                 tree.registerNode(this);
21526             }
21527         }
21528     },
21529
21530     /**
21531      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21532      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21533      * @return {String} The path
21534      */
21535     getPath : function(attr){
21536         attr = attr || "id";
21537         var p = this.parentNode;
21538         var b = [this.attributes[attr]];
21539         while(p){
21540             b.unshift(p.attributes[attr]);
21541             p = p.parentNode;
21542         }
21543         var sep = this.getOwnerTree().pathSeparator;
21544         return sep + b.join(sep);
21545     },
21546
21547     /**
21548      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21549      * function call will be the scope provided or the current node. The arguments to the function
21550      * will be the args provided or the current node. If the function returns false at any point,
21551      * the bubble is stopped.
21552      * @param {Function} fn The function to call
21553      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21554      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21555      */
21556     bubble : function(fn, scope, args){
21557         var p = this;
21558         while(p){
21559             if(fn.call(scope || p, args || p) === false){
21560                 break;
21561             }
21562             p = p.parentNode;
21563         }
21564     },
21565
21566     /**
21567      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21568      * function call will be the scope provided or the current node. The arguments to the function
21569      * will be the args provided or the current node. If the function returns false at any point,
21570      * the cascade is stopped on that branch.
21571      * @param {Function} fn The function to call
21572      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21573      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21574      */
21575     cascade : function(fn, scope, args){
21576         if(fn.call(scope || this, args || this) !== false){
21577             var cs = this.childNodes;
21578             for(var i = 0, len = cs.length; i < len; i++) {
21579                 cs[i].cascade(fn, scope, args);
21580             }
21581         }
21582     },
21583
21584     /**
21585      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21586      * function call will be the scope provided or the current node. The arguments to the function
21587      * will be the args provided or the current node. If the function returns false at any point,
21588      * the iteration stops.
21589      * @param {Function} fn The function to call
21590      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21591      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21592      */
21593     eachChild : function(fn, scope, args){
21594         var cs = this.childNodes;
21595         for(var i = 0, len = cs.length; i < len; i++) {
21596                 if(fn.call(scope || this, args || cs[i]) === false){
21597                     break;
21598                 }
21599         }
21600     },
21601
21602     /**
21603      * Finds the first child that has the attribute with the specified value.
21604      * @param {String} attribute The attribute name
21605      * @param {Mixed} value The value to search for
21606      * @return {Node} The found child or null if none was found
21607      */
21608     findChild : function(attribute, value){
21609         var cs = this.childNodes;
21610         for(var i = 0, len = cs.length; i < len; i++) {
21611                 if(cs[i].attributes[attribute] == value){
21612                     return cs[i];
21613                 }
21614         }
21615         return null;
21616     },
21617
21618     /**
21619      * Finds the first child by a custom function. The child matches if the function passed
21620      * returns true.
21621      * @param {Function} fn
21622      * @param {Object} scope (optional)
21623      * @return {Node} The found child or null if none was found
21624      */
21625     findChildBy : function(fn, scope){
21626         var cs = this.childNodes;
21627         for(var i = 0, len = cs.length; i < len; i++) {
21628                 if(fn.call(scope||cs[i], cs[i]) === true){
21629                     return cs[i];
21630                 }
21631         }
21632         return null;
21633     },
21634
21635     /**
21636      * Sorts this nodes children using the supplied sort function
21637      * @param {Function} fn
21638      * @param {Object} scope (optional)
21639      */
21640     sort : function(fn, scope){
21641         var cs = this.childNodes;
21642         var len = cs.length;
21643         if(len > 0){
21644             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21645             cs.sort(sortFn);
21646             for(var i = 0; i < len; i++){
21647                 var n = cs[i];
21648                 n.previousSibling = cs[i-1];
21649                 n.nextSibling = cs[i+1];
21650                 if(i == 0){
21651                     this.setFirstChild(n);
21652                 }
21653                 if(i == len-1){
21654                     this.setLastChild(n);
21655                 }
21656             }
21657         }
21658     },
21659
21660     /**
21661      * Returns true if this node is an ancestor (at any point) of the passed node.
21662      * @param {Node} node
21663      * @return {Boolean}
21664      */
21665     contains : function(node){
21666         return node.isAncestor(this);
21667     },
21668
21669     /**
21670      * Returns true if the passed node is an ancestor (at any point) of this node.
21671      * @param {Node} node
21672      * @return {Boolean}
21673      */
21674     isAncestor : function(node){
21675         var p = this.parentNode;
21676         while(p){
21677             if(p == node){
21678                 return true;
21679             }
21680             p = p.parentNode;
21681         }
21682         return false;
21683     },
21684
21685     toString : function(){
21686         return "[Node"+(this.id?" "+this.id:"")+"]";
21687     }
21688 });/*
21689  * Based on:
21690  * Ext JS Library 1.1.1
21691  * Copyright(c) 2006-2007, Ext JS, LLC.
21692  *
21693  * Originally Released Under LGPL - original licence link has changed is not relivant.
21694  *
21695  * Fork - LGPL
21696  * <script type="text/javascript">
21697  */
21698  
21699
21700 /**
21701  * @class Roo.ComponentMgr
21702  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21703  * @singleton
21704  */
21705 Roo.ComponentMgr = function(){
21706     var all = new Roo.util.MixedCollection();
21707
21708     return {
21709         /**
21710          * Registers a component.
21711          * @param {Roo.Component} c The component
21712          */
21713         register : function(c){
21714             all.add(c);
21715         },
21716
21717         /**
21718          * Unregisters a component.
21719          * @param {Roo.Component} c The component
21720          */
21721         unregister : function(c){
21722             all.remove(c);
21723         },
21724
21725         /**
21726          * Returns a component by id
21727          * @param {String} id The component id
21728          */
21729         get : function(id){
21730             return all.get(id);
21731         },
21732
21733         /**
21734          * Registers a function that will be called when a specified component is added to ComponentMgr
21735          * @param {String} id The component id
21736          * @param {Funtction} fn The callback function
21737          * @param {Object} scope The scope of the callback
21738          */
21739         onAvailable : function(id, fn, scope){
21740             all.on("add", function(index, o){
21741                 if(o.id == id){
21742                     fn.call(scope || o, o);
21743                     all.un("add", fn, scope);
21744                 }
21745             });
21746         }
21747     };
21748 }();/*
21749  * Based on:
21750  * Ext JS Library 1.1.1
21751  * Copyright(c) 2006-2007, Ext JS, LLC.
21752  *
21753  * Originally Released Under LGPL - original licence link has changed is not relivant.
21754  *
21755  * Fork - LGPL
21756  * <script type="text/javascript">
21757  */
21758  
21759 /**
21760  * @class Roo.Component
21761  * @extends Roo.util.Observable
21762  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21763  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21764  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21765  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21766  * All visual components (widgets) that require rendering into a layout should subclass Component.
21767  * @constructor
21768  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21769  * 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
21770  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21771  */
21772 Roo.Component = function(config){
21773     config = config || {};
21774     if(config.tagName || config.dom || typeof config == "string"){ // element object
21775         config = {el: config, id: config.id || config};
21776     }
21777     this.initialConfig = config;
21778
21779     Roo.apply(this, config);
21780     this.addEvents({
21781         /**
21782          * @event disable
21783          * Fires after the component is disabled.
21784              * @param {Roo.Component} this
21785              */
21786         disable : true,
21787         /**
21788          * @event enable
21789          * Fires after the component is enabled.
21790              * @param {Roo.Component} this
21791              */
21792         enable : true,
21793         /**
21794          * @event beforeshow
21795          * Fires before the component is shown.  Return false to stop the show.
21796              * @param {Roo.Component} this
21797              */
21798         beforeshow : true,
21799         /**
21800          * @event show
21801          * Fires after the component is shown.
21802              * @param {Roo.Component} this
21803              */
21804         show : true,
21805         /**
21806          * @event beforehide
21807          * Fires before the component is hidden. Return false to stop the hide.
21808              * @param {Roo.Component} this
21809              */
21810         beforehide : true,
21811         /**
21812          * @event hide
21813          * Fires after the component is hidden.
21814              * @param {Roo.Component} this
21815              */
21816         hide : true,
21817         /**
21818          * @event beforerender
21819          * Fires before the component is rendered. Return false to stop the render.
21820              * @param {Roo.Component} this
21821              */
21822         beforerender : true,
21823         /**
21824          * @event render
21825          * Fires after the component is rendered.
21826              * @param {Roo.Component} this
21827              */
21828         render : true,
21829         /**
21830          * @event beforedestroy
21831          * Fires before the component is destroyed. Return false to stop the destroy.
21832              * @param {Roo.Component} this
21833              */
21834         beforedestroy : true,
21835         /**
21836          * @event destroy
21837          * Fires after the component is destroyed.
21838              * @param {Roo.Component} this
21839              */
21840         destroy : true
21841     });
21842     if(!this.id){
21843         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21844     }
21845     Roo.ComponentMgr.register(this);
21846     Roo.Component.superclass.constructor.call(this);
21847     this.initComponent();
21848     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21849         this.render(this.renderTo);
21850         delete this.renderTo;
21851     }
21852 };
21853
21854 /** @private */
21855 Roo.Component.AUTO_ID = 1000;
21856
21857 Roo.extend(Roo.Component, Roo.util.Observable, {
21858     /**
21859      * @scope Roo.Component.prototype
21860      * @type {Boolean}
21861      * true if this component is hidden. Read-only.
21862      */
21863     hidden : false,
21864     /**
21865      * @type {Boolean}
21866      * true if this component is disabled. Read-only.
21867      */
21868     disabled : false,
21869     /**
21870      * @type {Boolean}
21871      * true if this component has been rendered. Read-only.
21872      */
21873     rendered : false,
21874     
21875     /** @cfg {String} disableClass
21876      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21877      */
21878     disabledClass : "x-item-disabled",
21879         /** @cfg {Boolean} allowDomMove
21880          * Whether the component can move the Dom node when rendering (defaults to true).
21881          */
21882     allowDomMove : true,
21883     /** @cfg {String} hideMode
21884      * How this component should hidden. Supported values are
21885      * "visibility" (css visibility), "offsets" (negative offset position) and
21886      * "display" (css display) - defaults to "display".
21887      */
21888     hideMode: 'display',
21889
21890     /** @private */
21891     ctype : "Roo.Component",
21892
21893     /**
21894      * @cfg {String} actionMode 
21895      * which property holds the element that used for  hide() / show() / disable() / enable()
21896      * default is 'el' 
21897      */
21898     actionMode : "el",
21899
21900     /** @private */
21901     getActionEl : function(){
21902         return this[this.actionMode];
21903     },
21904
21905     initComponent : Roo.emptyFn,
21906     /**
21907      * If this is a lazy rendering component, render it to its container element.
21908      * @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.
21909      */
21910     render : function(container, position){
21911         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21912             if(!container && this.el){
21913                 this.el = Roo.get(this.el);
21914                 container = this.el.dom.parentNode;
21915                 this.allowDomMove = false;
21916             }
21917             this.container = Roo.get(container);
21918             this.rendered = true;
21919             if(position !== undefined){
21920                 if(typeof position == 'number'){
21921                     position = this.container.dom.childNodes[position];
21922                 }else{
21923                     position = Roo.getDom(position);
21924                 }
21925             }
21926             this.onRender(this.container, position || null);
21927             if(this.cls){
21928                 this.el.addClass(this.cls);
21929                 delete this.cls;
21930             }
21931             if(this.style){
21932                 this.el.applyStyles(this.style);
21933                 delete this.style;
21934             }
21935             this.fireEvent("render", this);
21936             this.afterRender(this.container);
21937             if(this.hidden){
21938                 this.hide();
21939             }
21940             if(this.disabled){
21941                 this.disable();
21942             }
21943         }
21944         return this;
21945     },
21946
21947     /** @private */
21948     // default function is not really useful
21949     onRender : function(ct, position){
21950         if(this.el){
21951             this.el = Roo.get(this.el);
21952             if(this.allowDomMove !== false){
21953                 ct.dom.insertBefore(this.el.dom, position);
21954             }
21955         }
21956     },
21957
21958     /** @private */
21959     getAutoCreate : function(){
21960         var cfg = typeof this.autoCreate == "object" ?
21961                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21962         if(this.id && !cfg.id){
21963             cfg.id = this.id;
21964         }
21965         return cfg;
21966     },
21967
21968     /** @private */
21969     afterRender : Roo.emptyFn,
21970
21971     /**
21972      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21973      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21974      */
21975     destroy : function(){
21976         if(this.fireEvent("beforedestroy", this) !== false){
21977             this.purgeListeners();
21978             this.beforeDestroy();
21979             if(this.rendered){
21980                 this.el.removeAllListeners();
21981                 this.el.remove();
21982                 if(this.actionMode == "container"){
21983                     this.container.remove();
21984                 }
21985             }
21986             this.onDestroy();
21987             Roo.ComponentMgr.unregister(this);
21988             this.fireEvent("destroy", this);
21989         }
21990     },
21991
21992         /** @private */
21993     beforeDestroy : function(){
21994
21995     },
21996
21997         /** @private */
21998         onDestroy : function(){
21999
22000     },
22001
22002     /**
22003      * Returns the underlying {@link Roo.Element}.
22004      * @return {Roo.Element} The element
22005      */
22006     getEl : function(){
22007         return this.el;
22008     },
22009
22010     /**
22011      * Returns the id of this component.
22012      * @return {String}
22013      */
22014     getId : function(){
22015         return this.id;
22016     },
22017
22018     /**
22019      * Try to focus this component.
22020      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22021      * @return {Roo.Component} this
22022      */
22023     focus : function(selectText){
22024         if(this.rendered){
22025             this.el.focus();
22026             if(selectText === true){
22027                 this.el.dom.select();
22028             }
22029         }
22030         return this;
22031     },
22032
22033     /** @private */
22034     blur : function(){
22035         if(this.rendered){
22036             this.el.blur();
22037         }
22038         return this;
22039     },
22040
22041     /**
22042      * Disable this component.
22043      * @return {Roo.Component} this
22044      */
22045     disable : function(){
22046         if(this.rendered){
22047             this.onDisable();
22048         }
22049         this.disabled = true;
22050         this.fireEvent("disable", this);
22051         return this;
22052     },
22053
22054         // private
22055     onDisable : function(){
22056         this.getActionEl().addClass(this.disabledClass);
22057         this.el.dom.disabled = true;
22058     },
22059
22060     /**
22061      * Enable this component.
22062      * @return {Roo.Component} this
22063      */
22064     enable : function(){
22065         if(this.rendered){
22066             this.onEnable();
22067         }
22068         this.disabled = false;
22069         this.fireEvent("enable", this);
22070         return this;
22071     },
22072
22073         // private
22074     onEnable : function(){
22075         this.getActionEl().removeClass(this.disabledClass);
22076         this.el.dom.disabled = false;
22077     },
22078
22079     /**
22080      * Convenience function for setting disabled/enabled by boolean.
22081      * @param {Boolean} disabled
22082      */
22083     setDisabled : function(disabled){
22084         this[disabled ? "disable" : "enable"]();
22085     },
22086
22087     /**
22088      * Show this component.
22089      * @return {Roo.Component} this
22090      */
22091     show: function(){
22092         if(this.fireEvent("beforeshow", this) !== false){
22093             this.hidden = false;
22094             if(this.rendered){
22095                 this.onShow();
22096             }
22097             this.fireEvent("show", this);
22098         }
22099         return this;
22100     },
22101
22102     // private
22103     onShow : function(){
22104         var ae = this.getActionEl();
22105         if(this.hideMode == 'visibility'){
22106             ae.dom.style.visibility = "visible";
22107         }else if(this.hideMode == 'offsets'){
22108             ae.removeClass('x-hidden');
22109         }else{
22110             ae.dom.style.display = "";
22111         }
22112     },
22113
22114     /**
22115      * Hide this component.
22116      * @return {Roo.Component} this
22117      */
22118     hide: function(){
22119         if(this.fireEvent("beforehide", this) !== false){
22120             this.hidden = true;
22121             if(this.rendered){
22122                 this.onHide();
22123             }
22124             this.fireEvent("hide", this);
22125         }
22126         return this;
22127     },
22128
22129     // private
22130     onHide : function(){
22131         var ae = this.getActionEl();
22132         if(this.hideMode == 'visibility'){
22133             ae.dom.style.visibility = "hidden";
22134         }else if(this.hideMode == 'offsets'){
22135             ae.addClass('x-hidden');
22136         }else{
22137             ae.dom.style.display = "none";
22138         }
22139     },
22140
22141     /**
22142      * Convenience function to hide or show this component by boolean.
22143      * @param {Boolean} visible True to show, false to hide
22144      * @return {Roo.Component} this
22145      */
22146     setVisible: function(visible){
22147         if(visible) {
22148             this.show();
22149         }else{
22150             this.hide();
22151         }
22152         return this;
22153     },
22154
22155     /**
22156      * Returns true if this component is visible.
22157      */
22158     isVisible : function(){
22159         return this.getActionEl().isVisible();
22160     },
22161
22162     cloneConfig : function(overrides){
22163         overrides = overrides || {};
22164         var id = overrides.id || Roo.id();
22165         var cfg = Roo.applyIf(overrides, this.initialConfig);
22166         cfg.id = id; // prevent dup id
22167         return new this.constructor(cfg);
22168     }
22169 });/*
22170  * Based on:
22171  * Ext JS Library 1.1.1
22172  * Copyright(c) 2006-2007, Ext JS, LLC.
22173  *
22174  * Originally Released Under LGPL - original licence link has changed is not relivant.
22175  *
22176  * Fork - LGPL
22177  * <script type="text/javascript">
22178  */
22179  (function(){ 
22180 /**
22181  * @class Roo.Layer
22182  * @extends Roo.Element
22183  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22184  * automatic maintaining of shadow/shim positions.
22185  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22186  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22187  * you can pass a string with a CSS class name. False turns off the shadow.
22188  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22189  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22190  * @cfg {String} cls CSS class to add to the element
22191  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22192  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22193  * @constructor
22194  * @param {Object} config An object with config options.
22195  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22196  */
22197
22198 Roo.Layer = function(config, existingEl){
22199     config = config || {};
22200     var dh = Roo.DomHelper;
22201     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22202     if(existingEl){
22203         this.dom = Roo.getDom(existingEl);
22204     }
22205     if(!this.dom){
22206         var o = config.dh || {tag: "div", cls: "x-layer"};
22207         this.dom = dh.append(pel, o);
22208     }
22209     if(config.cls){
22210         this.addClass(config.cls);
22211     }
22212     this.constrain = config.constrain !== false;
22213     this.visibilityMode = Roo.Element.VISIBILITY;
22214     if(config.id){
22215         this.id = this.dom.id = config.id;
22216     }else{
22217         this.id = Roo.id(this.dom);
22218     }
22219     this.zindex = config.zindex || this.getZIndex();
22220     this.position("absolute", this.zindex);
22221     if(config.shadow){
22222         this.shadowOffset = config.shadowOffset || 4;
22223         this.shadow = new Roo.Shadow({
22224             offset : this.shadowOffset,
22225             mode : config.shadow
22226         });
22227     }else{
22228         this.shadowOffset = 0;
22229     }
22230     this.useShim = config.shim !== false && Roo.useShims;
22231     this.useDisplay = config.useDisplay;
22232     this.hide();
22233 };
22234
22235 var supr = Roo.Element.prototype;
22236
22237 // shims are shared among layer to keep from having 100 iframes
22238 var shims = [];
22239
22240 Roo.extend(Roo.Layer, Roo.Element, {
22241
22242     getZIndex : function(){
22243         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22244     },
22245
22246     getShim : function(){
22247         if(!this.useShim){
22248             return null;
22249         }
22250         if(this.shim){
22251             return this.shim;
22252         }
22253         var shim = shims.shift();
22254         if(!shim){
22255             shim = this.createShim();
22256             shim.enableDisplayMode('block');
22257             shim.dom.style.display = 'none';
22258             shim.dom.style.visibility = 'visible';
22259         }
22260         var pn = this.dom.parentNode;
22261         if(shim.dom.parentNode != pn){
22262             pn.insertBefore(shim.dom, this.dom);
22263         }
22264         shim.setStyle('z-index', this.getZIndex()-2);
22265         this.shim = shim;
22266         return shim;
22267     },
22268
22269     hideShim : function(){
22270         if(this.shim){
22271             this.shim.setDisplayed(false);
22272             shims.push(this.shim);
22273             delete this.shim;
22274         }
22275     },
22276
22277     disableShadow : function(){
22278         if(this.shadow){
22279             this.shadowDisabled = true;
22280             this.shadow.hide();
22281             this.lastShadowOffset = this.shadowOffset;
22282             this.shadowOffset = 0;
22283         }
22284     },
22285
22286     enableShadow : function(show){
22287         if(this.shadow){
22288             this.shadowDisabled = false;
22289             this.shadowOffset = this.lastShadowOffset;
22290             delete this.lastShadowOffset;
22291             if(show){
22292                 this.sync(true);
22293             }
22294         }
22295     },
22296
22297     // private
22298     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22299     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22300     sync : function(doShow){
22301         var sw = this.shadow;
22302         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22303             var sh = this.getShim();
22304
22305             var w = this.getWidth(),
22306                 h = this.getHeight();
22307
22308             var l = this.getLeft(true),
22309                 t = this.getTop(true);
22310
22311             if(sw && !this.shadowDisabled){
22312                 if(doShow && !sw.isVisible()){
22313                     sw.show(this);
22314                 }else{
22315                     sw.realign(l, t, w, h);
22316                 }
22317                 if(sh){
22318                     if(doShow){
22319                        sh.show();
22320                     }
22321                     // fit the shim behind the shadow, so it is shimmed too
22322                     var a = sw.adjusts, s = sh.dom.style;
22323                     s.left = (Math.min(l, l+a.l))+"px";
22324                     s.top = (Math.min(t, t+a.t))+"px";
22325                     s.width = (w+a.w)+"px";
22326                     s.height = (h+a.h)+"px";
22327                 }
22328             }else if(sh){
22329                 if(doShow){
22330                    sh.show();
22331                 }
22332                 sh.setSize(w, h);
22333                 sh.setLeftTop(l, t);
22334             }
22335             
22336         }
22337     },
22338
22339     // private
22340     destroy : function(){
22341         this.hideShim();
22342         if(this.shadow){
22343             this.shadow.hide();
22344         }
22345         this.removeAllListeners();
22346         var pn = this.dom.parentNode;
22347         if(pn){
22348             pn.removeChild(this.dom);
22349         }
22350         Roo.Element.uncache(this.id);
22351     },
22352
22353     remove : function(){
22354         this.destroy();
22355     },
22356
22357     // private
22358     beginUpdate : function(){
22359         this.updating = true;
22360     },
22361
22362     // private
22363     endUpdate : function(){
22364         this.updating = false;
22365         this.sync(true);
22366     },
22367
22368     // private
22369     hideUnders : function(negOffset){
22370         if(this.shadow){
22371             this.shadow.hide();
22372         }
22373         this.hideShim();
22374     },
22375
22376     // private
22377     constrainXY : function(){
22378         if(this.constrain){
22379             var vw = Roo.lib.Dom.getViewWidth(),
22380                 vh = Roo.lib.Dom.getViewHeight();
22381             var s = Roo.get(document).getScroll();
22382
22383             var xy = this.getXY();
22384             var x = xy[0], y = xy[1];   
22385             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22386             // only move it if it needs it
22387             var moved = false;
22388             // first validate right/bottom
22389             if((x + w) > vw+s.left){
22390                 x = vw - w - this.shadowOffset;
22391                 moved = true;
22392             }
22393             if((y + h) > vh+s.top){
22394                 y = vh - h - this.shadowOffset;
22395                 moved = true;
22396             }
22397             // then make sure top/left isn't negative
22398             if(x < s.left){
22399                 x = s.left;
22400                 moved = true;
22401             }
22402             if(y < s.top){
22403                 y = s.top;
22404                 moved = true;
22405             }
22406             if(moved){
22407                 if(this.avoidY){
22408                     var ay = this.avoidY;
22409                     if(y <= ay && (y+h) >= ay){
22410                         y = ay-h-5;   
22411                     }
22412                 }
22413                 xy = [x, y];
22414                 this.storeXY(xy);
22415                 supr.setXY.call(this, xy);
22416                 this.sync();
22417             }
22418         }
22419     },
22420
22421     isVisible : function(){
22422         return this.visible;    
22423     },
22424
22425     // private
22426     showAction : function(){
22427         this.visible = true; // track visibility to prevent getStyle calls
22428         if(this.useDisplay === true){
22429             this.setDisplayed("");
22430         }else if(this.lastXY){
22431             supr.setXY.call(this, this.lastXY);
22432         }else if(this.lastLT){
22433             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22434         }
22435     },
22436
22437     // private
22438     hideAction : function(){
22439         this.visible = false;
22440         if(this.useDisplay === true){
22441             this.setDisplayed(false);
22442         }else{
22443             this.setLeftTop(-10000,-10000);
22444         }
22445     },
22446
22447     // overridden Element method
22448     setVisible : function(v, a, d, c, e){
22449         if(v){
22450             this.showAction();
22451         }
22452         if(a && v){
22453             var cb = function(){
22454                 this.sync(true);
22455                 if(c){
22456                     c();
22457                 }
22458             }.createDelegate(this);
22459             supr.setVisible.call(this, true, true, d, cb, e);
22460         }else{
22461             if(!v){
22462                 this.hideUnders(true);
22463             }
22464             var cb = c;
22465             if(a){
22466                 cb = function(){
22467                     this.hideAction();
22468                     if(c){
22469                         c();
22470                     }
22471                 }.createDelegate(this);
22472             }
22473             supr.setVisible.call(this, v, a, d, cb, e);
22474             if(v){
22475                 this.sync(true);
22476             }else if(!a){
22477                 this.hideAction();
22478             }
22479         }
22480     },
22481
22482     storeXY : function(xy){
22483         delete this.lastLT;
22484         this.lastXY = xy;
22485     },
22486
22487     storeLeftTop : function(left, top){
22488         delete this.lastXY;
22489         this.lastLT = [left, top];
22490     },
22491
22492     // private
22493     beforeFx : function(){
22494         this.beforeAction();
22495         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22496     },
22497
22498     // private
22499     afterFx : function(){
22500         Roo.Layer.superclass.afterFx.apply(this, arguments);
22501         this.sync(this.isVisible());
22502     },
22503
22504     // private
22505     beforeAction : function(){
22506         if(!this.updating && this.shadow){
22507             this.shadow.hide();
22508         }
22509     },
22510
22511     // overridden Element method
22512     setLeft : function(left){
22513         this.storeLeftTop(left, this.getTop(true));
22514         supr.setLeft.apply(this, arguments);
22515         this.sync();
22516     },
22517
22518     setTop : function(top){
22519         this.storeLeftTop(this.getLeft(true), top);
22520         supr.setTop.apply(this, arguments);
22521         this.sync();
22522     },
22523
22524     setLeftTop : function(left, top){
22525         this.storeLeftTop(left, top);
22526         supr.setLeftTop.apply(this, arguments);
22527         this.sync();
22528     },
22529
22530     setXY : function(xy, a, d, c, e){
22531         this.fixDisplay();
22532         this.beforeAction();
22533         this.storeXY(xy);
22534         var cb = this.createCB(c);
22535         supr.setXY.call(this, xy, a, d, cb, e);
22536         if(!a){
22537             cb();
22538         }
22539     },
22540
22541     // private
22542     createCB : function(c){
22543         var el = this;
22544         return function(){
22545             el.constrainXY();
22546             el.sync(true);
22547             if(c){
22548                 c();
22549             }
22550         };
22551     },
22552
22553     // overridden Element method
22554     setX : function(x, a, d, c, e){
22555         this.setXY([x, this.getY()], a, d, c, e);
22556     },
22557
22558     // overridden Element method
22559     setY : function(y, a, d, c, e){
22560         this.setXY([this.getX(), y], a, d, c, e);
22561     },
22562
22563     // overridden Element method
22564     setSize : function(w, h, a, d, c, e){
22565         this.beforeAction();
22566         var cb = this.createCB(c);
22567         supr.setSize.call(this, w, h, a, d, cb, e);
22568         if(!a){
22569             cb();
22570         }
22571     },
22572
22573     // overridden Element method
22574     setWidth : function(w, a, d, c, e){
22575         this.beforeAction();
22576         var cb = this.createCB(c);
22577         supr.setWidth.call(this, w, a, d, cb, e);
22578         if(!a){
22579             cb();
22580         }
22581     },
22582
22583     // overridden Element method
22584     setHeight : function(h, a, d, c, e){
22585         this.beforeAction();
22586         var cb = this.createCB(c);
22587         supr.setHeight.call(this, h, a, d, cb, e);
22588         if(!a){
22589             cb();
22590         }
22591     },
22592
22593     // overridden Element method
22594     setBounds : function(x, y, w, h, a, d, c, e){
22595         this.beforeAction();
22596         var cb = this.createCB(c);
22597         if(!a){
22598             this.storeXY([x, y]);
22599             supr.setXY.call(this, [x, y]);
22600             supr.setSize.call(this, w, h, a, d, cb, e);
22601             cb();
22602         }else{
22603             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22604         }
22605         return this;
22606     },
22607     
22608     /**
22609      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22610      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22611      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22612      * @param {Number} zindex The new z-index to set
22613      * @return {this} The Layer
22614      */
22615     setZIndex : function(zindex){
22616         this.zindex = zindex;
22617         this.setStyle("z-index", zindex + 2);
22618         if(this.shadow){
22619             this.shadow.setZIndex(zindex + 1);
22620         }
22621         if(this.shim){
22622             this.shim.setStyle("z-index", zindex);
22623         }
22624     }
22625 });
22626 })();/*
22627  * Based on:
22628  * Ext JS Library 1.1.1
22629  * Copyright(c) 2006-2007, Ext JS, LLC.
22630  *
22631  * Originally Released Under LGPL - original licence link has changed is not relivant.
22632  *
22633  * Fork - LGPL
22634  * <script type="text/javascript">
22635  */
22636
22637
22638 /**
22639  * @class Roo.Shadow
22640  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22641  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22642  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22643  * @constructor
22644  * Create a new Shadow
22645  * @param {Object} config The config object
22646  */
22647 Roo.Shadow = function(config){
22648     Roo.apply(this, config);
22649     if(typeof this.mode != "string"){
22650         this.mode = this.defaultMode;
22651     }
22652     var o = this.offset, a = {h: 0};
22653     var rad = Math.floor(this.offset/2);
22654     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22655         case "drop":
22656             a.w = 0;
22657             a.l = a.t = o;
22658             a.t -= 1;
22659             if(Roo.isIE){
22660                 a.l -= this.offset + rad;
22661                 a.t -= this.offset + rad;
22662                 a.w -= rad;
22663                 a.h -= rad;
22664                 a.t += 1;
22665             }
22666         break;
22667         case "sides":
22668             a.w = (o*2);
22669             a.l = -o;
22670             a.t = o-1;
22671             if(Roo.isIE){
22672                 a.l -= (this.offset - rad);
22673                 a.t -= this.offset + rad;
22674                 a.l += 1;
22675                 a.w -= (this.offset - rad)*2;
22676                 a.w -= rad + 1;
22677                 a.h -= 1;
22678             }
22679         break;
22680         case "frame":
22681             a.w = a.h = (o*2);
22682             a.l = a.t = -o;
22683             a.t += 1;
22684             a.h -= 2;
22685             if(Roo.isIE){
22686                 a.l -= (this.offset - rad);
22687                 a.t -= (this.offset - rad);
22688                 a.l += 1;
22689                 a.w -= (this.offset + rad + 1);
22690                 a.h -= (this.offset + rad);
22691                 a.h += 1;
22692             }
22693         break;
22694     };
22695
22696     this.adjusts = a;
22697 };
22698
22699 Roo.Shadow.prototype = {
22700     /**
22701      * @cfg {String} mode
22702      * The shadow display mode.  Supports the following options:<br />
22703      * sides: Shadow displays on both sides and bottom only<br />
22704      * frame: Shadow displays equally on all four sides<br />
22705      * drop: Traditional bottom-right drop shadow (default)
22706      */
22707     /**
22708      * @cfg {String} offset
22709      * The number of pixels to offset the shadow from the element (defaults to 4)
22710      */
22711     offset: 4,
22712
22713     // private
22714     defaultMode: "drop",
22715
22716     /**
22717      * Displays the shadow under the target element
22718      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22719      */
22720     show : function(target){
22721         target = Roo.get(target);
22722         if(!this.el){
22723             this.el = Roo.Shadow.Pool.pull();
22724             if(this.el.dom.nextSibling != target.dom){
22725                 this.el.insertBefore(target);
22726             }
22727         }
22728         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22729         if(Roo.isIE){
22730             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22731         }
22732         this.realign(
22733             target.getLeft(true),
22734             target.getTop(true),
22735             target.getWidth(),
22736             target.getHeight()
22737         );
22738         this.el.dom.style.display = "block";
22739     },
22740
22741     /**
22742      * Returns true if the shadow is visible, else false
22743      */
22744     isVisible : function(){
22745         return this.el ? true : false;  
22746     },
22747
22748     /**
22749      * Direct alignment when values are already available. Show must be called at least once before
22750      * calling this method to ensure it is initialized.
22751      * @param {Number} left The target element left position
22752      * @param {Number} top The target element top position
22753      * @param {Number} width The target element width
22754      * @param {Number} height The target element height
22755      */
22756     realign : function(l, t, w, h){
22757         if(!this.el){
22758             return;
22759         }
22760         var a = this.adjusts, d = this.el.dom, s = d.style;
22761         var iea = 0;
22762         s.left = (l+a.l)+"px";
22763         s.top = (t+a.t)+"px";
22764         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22765  
22766         if(s.width != sws || s.height != shs){
22767             s.width = sws;
22768             s.height = shs;
22769             if(!Roo.isIE){
22770                 var cn = d.childNodes;
22771                 var sww = Math.max(0, (sw-12))+"px";
22772                 cn[0].childNodes[1].style.width = sww;
22773                 cn[1].childNodes[1].style.width = sww;
22774                 cn[2].childNodes[1].style.width = sww;
22775                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22776             }
22777         }
22778     },
22779
22780     /**
22781      * Hides this shadow
22782      */
22783     hide : function(){
22784         if(this.el){
22785             this.el.dom.style.display = "none";
22786             Roo.Shadow.Pool.push(this.el);
22787             delete this.el;
22788         }
22789     },
22790
22791     /**
22792      * Adjust the z-index of this shadow
22793      * @param {Number} zindex The new z-index
22794      */
22795     setZIndex : function(z){
22796         this.zIndex = z;
22797         if(this.el){
22798             this.el.setStyle("z-index", z);
22799         }
22800     }
22801 };
22802
22803 // Private utility class that manages the internal Shadow cache
22804 Roo.Shadow.Pool = function(){
22805     var p = [];
22806     var markup = Roo.isIE ?
22807                  '<div class="x-ie-shadow"></div>' :
22808                  '<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>';
22809     return {
22810         pull : function(){
22811             var sh = p.shift();
22812             if(!sh){
22813                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22814                 sh.autoBoxAdjust = false;
22815             }
22816             return sh;
22817         },
22818
22819         push : function(sh){
22820             p.push(sh);
22821         }
22822     };
22823 }();/*
22824  * Based on:
22825  * Ext JS Library 1.1.1
22826  * Copyright(c) 2006-2007, Ext JS, LLC.
22827  *
22828  * Originally Released Under LGPL - original licence link has changed is not relivant.
22829  *
22830  * Fork - LGPL
22831  * <script type="text/javascript">
22832  */
22833
22834 /**
22835  * @class Roo.BoxComponent
22836  * @extends Roo.Component
22837  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22838  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22839  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22840  * layout containers.
22841  * @constructor
22842  * @param {Roo.Element/String/Object} config The configuration options.
22843  */
22844 Roo.BoxComponent = function(config){
22845     Roo.Component.call(this, config);
22846     this.addEvents({
22847         /**
22848          * @event resize
22849          * Fires after the component is resized.
22850              * @param {Roo.Component} this
22851              * @param {Number} adjWidth The box-adjusted width that was set
22852              * @param {Number} adjHeight The box-adjusted height that was set
22853              * @param {Number} rawWidth The width that was originally specified
22854              * @param {Number} rawHeight The height that was originally specified
22855              */
22856         resize : true,
22857         /**
22858          * @event move
22859          * Fires after the component is moved.
22860              * @param {Roo.Component} this
22861              * @param {Number} x The new x position
22862              * @param {Number} y The new y position
22863              */
22864         move : true
22865     });
22866 };
22867
22868 Roo.extend(Roo.BoxComponent, Roo.Component, {
22869     // private, set in afterRender to signify that the component has been rendered
22870     boxReady : false,
22871     // private, used to defer height settings to subclasses
22872     deferHeight: false,
22873     /** @cfg {Number} width
22874      * width (optional) size of component
22875      */
22876      /** @cfg {Number} height
22877      * height (optional) size of component
22878      */
22879      
22880     /**
22881      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22882      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22883      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22884      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22885      * @return {Roo.BoxComponent} this
22886      */
22887     setSize : function(w, h){
22888         // support for standard size objects
22889         if(typeof w == 'object'){
22890             h = w.height;
22891             w = w.width;
22892         }
22893         // not rendered
22894         if(!this.boxReady){
22895             this.width = w;
22896             this.height = h;
22897             return this;
22898         }
22899
22900         // prevent recalcs when not needed
22901         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22902             return this;
22903         }
22904         this.lastSize = {width: w, height: h};
22905
22906         var adj = this.adjustSize(w, h);
22907         var aw = adj.width, ah = adj.height;
22908         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22909             var rz = this.getResizeEl();
22910             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22911                 rz.setSize(aw, ah);
22912             }else if(!this.deferHeight && ah !== undefined){
22913                 rz.setHeight(ah);
22914             }else if(aw !== undefined){
22915                 rz.setWidth(aw);
22916             }
22917             this.onResize(aw, ah, w, h);
22918             this.fireEvent('resize', this, aw, ah, w, h);
22919         }
22920         return this;
22921     },
22922
22923     /**
22924      * Gets the current size of the component's underlying element.
22925      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22926      */
22927     getSize : function(){
22928         return this.el.getSize();
22929     },
22930
22931     /**
22932      * Gets the current XY position of the component's underlying element.
22933      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22934      * @return {Array} The XY position of the element (e.g., [100, 200])
22935      */
22936     getPosition : function(local){
22937         if(local === true){
22938             return [this.el.getLeft(true), this.el.getTop(true)];
22939         }
22940         return this.xy || this.el.getXY();
22941     },
22942
22943     /**
22944      * Gets the current box measurements of the component's underlying element.
22945      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22946      * @returns {Object} box An object in the format {x, y, width, height}
22947      */
22948     getBox : function(local){
22949         var s = this.el.getSize();
22950         if(local){
22951             s.x = this.el.getLeft(true);
22952             s.y = this.el.getTop(true);
22953         }else{
22954             var xy = this.xy || this.el.getXY();
22955             s.x = xy[0];
22956             s.y = xy[1];
22957         }
22958         return s;
22959     },
22960
22961     /**
22962      * Sets the current box measurements of the component's underlying element.
22963      * @param {Object} box An object in the format {x, y, width, height}
22964      * @returns {Roo.BoxComponent} this
22965      */
22966     updateBox : function(box){
22967         this.setSize(box.width, box.height);
22968         this.setPagePosition(box.x, box.y);
22969         return this;
22970     },
22971
22972     // protected
22973     getResizeEl : function(){
22974         return this.resizeEl || this.el;
22975     },
22976
22977     // protected
22978     getPositionEl : function(){
22979         return this.positionEl || this.el;
22980     },
22981
22982     /**
22983      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22984      * This method fires the move event.
22985      * @param {Number} left The new left
22986      * @param {Number} top The new top
22987      * @returns {Roo.BoxComponent} this
22988      */
22989     setPosition : function(x, y){
22990         this.x = x;
22991         this.y = y;
22992         if(!this.boxReady){
22993             return this;
22994         }
22995         var adj = this.adjustPosition(x, y);
22996         var ax = adj.x, ay = adj.y;
22997
22998         var el = this.getPositionEl();
22999         if(ax !== undefined || ay !== undefined){
23000             if(ax !== undefined && ay !== undefined){
23001                 el.setLeftTop(ax, ay);
23002             }else if(ax !== undefined){
23003                 el.setLeft(ax);
23004             }else if(ay !== undefined){
23005                 el.setTop(ay);
23006             }
23007             this.onPosition(ax, ay);
23008             this.fireEvent('move', this, ax, ay);
23009         }
23010         return this;
23011     },
23012
23013     /**
23014      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23015      * This method fires the move event.
23016      * @param {Number} x The new x position
23017      * @param {Number} y The new y position
23018      * @returns {Roo.BoxComponent} this
23019      */
23020     setPagePosition : function(x, y){
23021         this.pageX = x;
23022         this.pageY = y;
23023         if(!this.boxReady){
23024             return;
23025         }
23026         if(x === undefined || y === undefined){ // cannot translate undefined points
23027             return;
23028         }
23029         var p = this.el.translatePoints(x, y);
23030         this.setPosition(p.left, p.top);
23031         return this;
23032     },
23033
23034     // private
23035     onRender : function(ct, position){
23036         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23037         if(this.resizeEl){
23038             this.resizeEl = Roo.get(this.resizeEl);
23039         }
23040         if(this.positionEl){
23041             this.positionEl = Roo.get(this.positionEl);
23042         }
23043     },
23044
23045     // private
23046     afterRender : function(){
23047         Roo.BoxComponent.superclass.afterRender.call(this);
23048         this.boxReady = true;
23049         this.setSize(this.width, this.height);
23050         if(this.x || this.y){
23051             this.setPosition(this.x, this.y);
23052         }
23053         if(this.pageX || this.pageY){
23054             this.setPagePosition(this.pageX, this.pageY);
23055         }
23056     },
23057
23058     /**
23059      * Force the component's size to recalculate based on the underlying element's current height and width.
23060      * @returns {Roo.BoxComponent} this
23061      */
23062     syncSize : function(){
23063         delete this.lastSize;
23064         this.setSize(this.el.getWidth(), this.el.getHeight());
23065         return this;
23066     },
23067
23068     /**
23069      * Called after the component is resized, this method is empty by default but can be implemented by any
23070      * subclass that needs to perform custom logic after a resize occurs.
23071      * @param {Number} adjWidth The box-adjusted width that was set
23072      * @param {Number} adjHeight The box-adjusted height that was set
23073      * @param {Number} rawWidth The width that was originally specified
23074      * @param {Number} rawHeight The height that was originally specified
23075      */
23076     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23077
23078     },
23079
23080     /**
23081      * Called after the component is moved, this method is empty by default but can be implemented by any
23082      * subclass that needs to perform custom logic after a move occurs.
23083      * @param {Number} x The new x position
23084      * @param {Number} y The new y position
23085      */
23086     onPosition : function(x, y){
23087
23088     },
23089
23090     // private
23091     adjustSize : function(w, h){
23092         if(this.autoWidth){
23093             w = 'auto';
23094         }
23095         if(this.autoHeight){
23096             h = 'auto';
23097         }
23098         return {width : w, height: h};
23099     },
23100
23101     // private
23102     adjustPosition : function(x, y){
23103         return {x : x, y: y};
23104     }
23105 });/*
23106  * Based on:
23107  * Ext JS Library 1.1.1
23108  * Copyright(c) 2006-2007, Ext JS, LLC.
23109  *
23110  * Originally Released Under LGPL - original licence link has changed is not relivant.
23111  *
23112  * Fork - LGPL
23113  * <script type="text/javascript">
23114  */
23115
23116
23117 /**
23118  * @class Roo.SplitBar
23119  * @extends Roo.util.Observable
23120  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23121  * <br><br>
23122  * Usage:
23123  * <pre><code>
23124 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23125                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23126 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23127 split.minSize = 100;
23128 split.maxSize = 600;
23129 split.animate = true;
23130 split.on('moved', splitterMoved);
23131 </code></pre>
23132  * @constructor
23133  * Create a new SplitBar
23134  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23135  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23136  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23137  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23138                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23139                         position of the SplitBar).
23140  */
23141 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23142     
23143     /** @private */
23144     this.el = Roo.get(dragElement, true);
23145     this.el.dom.unselectable = "on";
23146     /** @private */
23147     this.resizingEl = Roo.get(resizingElement, true);
23148
23149     /**
23150      * @private
23151      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23152      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23153      * @type Number
23154      */
23155     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23156     
23157     /**
23158      * The minimum size of the resizing element. (Defaults to 0)
23159      * @type Number
23160      */
23161     this.minSize = 0;
23162     
23163     /**
23164      * The maximum size of the resizing element. (Defaults to 2000)
23165      * @type Number
23166      */
23167     this.maxSize = 2000;
23168     
23169     /**
23170      * Whether to animate the transition to the new size
23171      * @type Boolean
23172      */
23173     this.animate = false;
23174     
23175     /**
23176      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23177      * @type Boolean
23178      */
23179     this.useShim = false;
23180     
23181     /** @private */
23182     this.shim = null;
23183     
23184     if(!existingProxy){
23185         /** @private */
23186         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23187     }else{
23188         this.proxy = Roo.get(existingProxy).dom;
23189     }
23190     /** @private */
23191     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23192     
23193     /** @private */
23194     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23195     
23196     /** @private */
23197     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23198     
23199     /** @private */
23200     this.dragSpecs = {};
23201     
23202     /**
23203      * @private The adapter to use to positon and resize elements
23204      */
23205     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23206     this.adapter.init(this);
23207     
23208     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23209         /** @private */
23210         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23211         this.el.addClass("x-splitbar-h");
23212     }else{
23213         /** @private */
23214         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23215         this.el.addClass("x-splitbar-v");
23216     }
23217     
23218     this.addEvents({
23219         /**
23220          * @event resize
23221          * Fires when the splitter is moved (alias for {@link #event-moved})
23222          * @param {Roo.SplitBar} this
23223          * @param {Number} newSize the new width or height
23224          */
23225         "resize" : true,
23226         /**
23227          * @event moved
23228          * Fires when the splitter is moved
23229          * @param {Roo.SplitBar} this
23230          * @param {Number} newSize the new width or height
23231          */
23232         "moved" : true,
23233         /**
23234          * @event beforeresize
23235          * Fires before the splitter is dragged
23236          * @param {Roo.SplitBar} this
23237          */
23238         "beforeresize" : true,
23239
23240         "beforeapply" : true
23241     });
23242
23243     Roo.util.Observable.call(this);
23244 };
23245
23246 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23247     onStartProxyDrag : function(x, y){
23248         this.fireEvent("beforeresize", this);
23249         if(!this.overlay){
23250             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23251             o.unselectable();
23252             o.enableDisplayMode("block");
23253             // all splitbars share the same overlay
23254             Roo.SplitBar.prototype.overlay = o;
23255         }
23256         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23257         this.overlay.show();
23258         Roo.get(this.proxy).setDisplayed("block");
23259         var size = this.adapter.getElementSize(this);
23260         this.activeMinSize = this.getMinimumSize();;
23261         this.activeMaxSize = this.getMaximumSize();;
23262         var c1 = size - this.activeMinSize;
23263         var c2 = Math.max(this.activeMaxSize - size, 0);
23264         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23265             this.dd.resetConstraints();
23266             this.dd.setXConstraint(
23267                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23268                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23269             );
23270             this.dd.setYConstraint(0, 0);
23271         }else{
23272             this.dd.resetConstraints();
23273             this.dd.setXConstraint(0, 0);
23274             this.dd.setYConstraint(
23275                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23276                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23277             );
23278          }
23279         this.dragSpecs.startSize = size;
23280         this.dragSpecs.startPoint = [x, y];
23281         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23282     },
23283     
23284     /** 
23285      * @private Called after the drag operation by the DDProxy
23286      */
23287     onEndProxyDrag : function(e){
23288         Roo.get(this.proxy).setDisplayed(false);
23289         var endPoint = Roo.lib.Event.getXY(e);
23290         if(this.overlay){
23291             this.overlay.hide();
23292         }
23293         var newSize;
23294         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23295             newSize = this.dragSpecs.startSize + 
23296                 (this.placement == Roo.SplitBar.LEFT ?
23297                     endPoint[0] - this.dragSpecs.startPoint[0] :
23298                     this.dragSpecs.startPoint[0] - endPoint[0]
23299                 );
23300         }else{
23301             newSize = this.dragSpecs.startSize + 
23302                 (this.placement == Roo.SplitBar.TOP ?
23303                     endPoint[1] - this.dragSpecs.startPoint[1] :
23304                     this.dragSpecs.startPoint[1] - endPoint[1]
23305                 );
23306         }
23307         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23308         if(newSize != this.dragSpecs.startSize){
23309             if(this.fireEvent('beforeapply', this, newSize) !== false){
23310                 this.adapter.setElementSize(this, newSize);
23311                 this.fireEvent("moved", this, newSize);
23312                 this.fireEvent("resize", this, newSize);
23313             }
23314         }
23315     },
23316     
23317     /**
23318      * Get the adapter this SplitBar uses
23319      * @return The adapter object
23320      */
23321     getAdapter : function(){
23322         return this.adapter;
23323     },
23324     
23325     /**
23326      * Set the adapter this SplitBar uses
23327      * @param {Object} adapter A SplitBar adapter object
23328      */
23329     setAdapter : function(adapter){
23330         this.adapter = adapter;
23331         this.adapter.init(this);
23332     },
23333     
23334     /**
23335      * Gets the minimum size for the resizing element
23336      * @return {Number} The minimum size
23337      */
23338     getMinimumSize : function(){
23339         return this.minSize;
23340     },
23341     
23342     /**
23343      * Sets the minimum size for the resizing element
23344      * @param {Number} minSize The minimum size
23345      */
23346     setMinimumSize : function(minSize){
23347         this.minSize = minSize;
23348     },
23349     
23350     /**
23351      * Gets the maximum size for the resizing element
23352      * @return {Number} The maximum size
23353      */
23354     getMaximumSize : function(){
23355         return this.maxSize;
23356     },
23357     
23358     /**
23359      * Sets the maximum size for the resizing element
23360      * @param {Number} maxSize The maximum size
23361      */
23362     setMaximumSize : function(maxSize){
23363         this.maxSize = maxSize;
23364     },
23365     
23366     /**
23367      * Sets the initialize size for the resizing element
23368      * @param {Number} size The initial size
23369      */
23370     setCurrentSize : function(size){
23371         var oldAnimate = this.animate;
23372         this.animate = false;
23373         this.adapter.setElementSize(this, size);
23374         this.animate = oldAnimate;
23375     },
23376     
23377     /**
23378      * Destroy this splitbar. 
23379      * @param {Boolean} removeEl True to remove the element
23380      */
23381     destroy : function(removeEl){
23382         if(this.shim){
23383             this.shim.remove();
23384         }
23385         this.dd.unreg();
23386         this.proxy.parentNode.removeChild(this.proxy);
23387         if(removeEl){
23388             this.el.remove();
23389         }
23390     }
23391 });
23392
23393 /**
23394  * @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.
23395  */
23396 Roo.SplitBar.createProxy = function(dir){
23397     var proxy = new Roo.Element(document.createElement("div"));
23398     proxy.unselectable();
23399     var cls = 'x-splitbar-proxy';
23400     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23401     document.body.appendChild(proxy.dom);
23402     return proxy.dom;
23403 };
23404
23405 /** 
23406  * @class Roo.SplitBar.BasicLayoutAdapter
23407  * Default Adapter. It assumes the splitter and resizing element are not positioned
23408  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23409  */
23410 Roo.SplitBar.BasicLayoutAdapter = function(){
23411 };
23412
23413 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23414     // do nothing for now
23415     init : function(s){
23416     
23417     },
23418     /**
23419      * Called before drag operations to get the current size of the resizing element. 
23420      * @param {Roo.SplitBar} s The SplitBar using this adapter
23421      */
23422      getElementSize : function(s){
23423         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23424             return s.resizingEl.getWidth();
23425         }else{
23426             return s.resizingEl.getHeight();
23427         }
23428     },
23429     
23430     /**
23431      * Called after drag operations to set the size of the resizing element.
23432      * @param {Roo.SplitBar} s The SplitBar using this adapter
23433      * @param {Number} newSize The new size to set
23434      * @param {Function} onComplete A function to be invoked when resizing is complete
23435      */
23436     setElementSize : function(s, newSize, onComplete){
23437         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23438             if(!s.animate){
23439                 s.resizingEl.setWidth(newSize);
23440                 if(onComplete){
23441                     onComplete(s, newSize);
23442                 }
23443             }else{
23444                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23445             }
23446         }else{
23447             
23448             if(!s.animate){
23449                 s.resizingEl.setHeight(newSize);
23450                 if(onComplete){
23451                     onComplete(s, newSize);
23452                 }
23453             }else{
23454                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23455             }
23456         }
23457     }
23458 };
23459
23460 /** 
23461  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23462  * @extends Roo.SplitBar.BasicLayoutAdapter
23463  * Adapter that  moves the splitter element to align with the resized sizing element. 
23464  * Used with an absolute positioned SplitBar.
23465  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23466  * document.body, make sure you assign an id to the body element.
23467  */
23468 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23469     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23470     this.container = Roo.get(container);
23471 };
23472
23473 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23474     init : function(s){
23475         this.basic.init(s);
23476     },
23477     
23478     getElementSize : function(s){
23479         return this.basic.getElementSize(s);
23480     },
23481     
23482     setElementSize : function(s, newSize, onComplete){
23483         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23484     },
23485     
23486     moveSplitter : function(s){
23487         var yes = Roo.SplitBar;
23488         switch(s.placement){
23489             case yes.LEFT:
23490                 s.el.setX(s.resizingEl.getRight());
23491                 break;
23492             case yes.RIGHT:
23493                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23494                 break;
23495             case yes.TOP:
23496                 s.el.setY(s.resizingEl.getBottom());
23497                 break;
23498             case yes.BOTTOM:
23499                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23500                 break;
23501         }
23502     }
23503 };
23504
23505 /**
23506  * Orientation constant - Create a vertical SplitBar
23507  * @static
23508  * @type Number
23509  */
23510 Roo.SplitBar.VERTICAL = 1;
23511
23512 /**
23513  * Orientation constant - Create a horizontal SplitBar
23514  * @static
23515  * @type Number
23516  */
23517 Roo.SplitBar.HORIZONTAL = 2;
23518
23519 /**
23520  * Placement constant - The resizing element is to the left of the splitter element
23521  * @static
23522  * @type Number
23523  */
23524 Roo.SplitBar.LEFT = 1;
23525
23526 /**
23527  * Placement constant - The resizing element is to the right of the splitter element
23528  * @static
23529  * @type Number
23530  */
23531 Roo.SplitBar.RIGHT = 2;
23532
23533 /**
23534  * Placement constant - The resizing element is positioned above the splitter element
23535  * @static
23536  * @type Number
23537  */
23538 Roo.SplitBar.TOP = 3;
23539
23540 /**
23541  * Placement constant - The resizing element is positioned under splitter element
23542  * @static
23543  * @type Number
23544  */
23545 Roo.SplitBar.BOTTOM = 4;
23546 /*
23547  * Based on:
23548  * Ext JS Library 1.1.1
23549  * Copyright(c) 2006-2007, Ext JS, LLC.
23550  *
23551  * Originally Released Under LGPL - original licence link has changed is not relivant.
23552  *
23553  * Fork - LGPL
23554  * <script type="text/javascript">
23555  */
23556
23557 /**
23558  * @class Roo.View
23559  * @extends Roo.util.Observable
23560  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23561  * This class also supports single and multi selection modes. <br>
23562  * Create a data model bound view:
23563  <pre><code>
23564  var store = new Roo.data.Store(...);
23565
23566  var view = new Roo.View({
23567     el : "my-element",
23568     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23569  
23570     singleSelect: true,
23571     selectedClass: "ydataview-selected",
23572     store: store
23573  });
23574
23575  // listen for node click?
23576  view.on("click", function(vw, index, node, e){
23577  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23578  });
23579
23580  // load XML data
23581  dataModel.load("foobar.xml");
23582  </code></pre>
23583  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23584  * <br><br>
23585  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23586  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23587  * 
23588  * Note: old style constructor is still suported (container, template, config)
23589  * 
23590  * @constructor
23591  * Create a new View
23592  * @param {Object} config The config object
23593  * 
23594  */
23595 Roo.View = function(config, depreciated_tpl, depreciated_config){
23596     
23597     if (typeof(depreciated_tpl) == 'undefined') {
23598         // new way.. - universal constructor.
23599         Roo.apply(this, config);
23600         this.el  = Roo.get(this.el);
23601     } else {
23602         // old format..
23603         this.el  = Roo.get(config);
23604         this.tpl = depreciated_tpl;
23605         Roo.apply(this, depreciated_config);
23606     }
23607      
23608     
23609     if(typeof(this.tpl) == "string"){
23610         this.tpl = new Roo.Template(this.tpl);
23611     } else {
23612         // support xtype ctors..
23613         this.tpl = new Roo.factory(this.tpl, Roo);
23614     }
23615     
23616     
23617     this.tpl.compile();
23618    
23619
23620      
23621     /** @private */
23622     this.addEvents({
23623         /**
23624          * @event beforeclick
23625          * Fires before a click is processed. Returns false to cancel the default action.
23626          * @param {Roo.View} this
23627          * @param {Number} index The index of the target node
23628          * @param {HTMLElement} node The target node
23629          * @param {Roo.EventObject} e The raw event object
23630          */
23631             "beforeclick" : true,
23632         /**
23633          * @event click
23634          * Fires when a template node is clicked.
23635          * @param {Roo.View} this
23636          * @param {Number} index The index of the target node
23637          * @param {HTMLElement} node The target node
23638          * @param {Roo.EventObject} e The raw event object
23639          */
23640             "click" : true,
23641         /**
23642          * @event dblclick
23643          * Fires when a template node is double clicked.
23644          * @param {Roo.View} this
23645          * @param {Number} index The index of the target node
23646          * @param {HTMLElement} node The target node
23647          * @param {Roo.EventObject} e The raw event object
23648          */
23649             "dblclick" : true,
23650         /**
23651          * @event contextmenu
23652          * Fires when a template node is right clicked.
23653          * @param {Roo.View} this
23654          * @param {Number} index The index of the target node
23655          * @param {HTMLElement} node The target node
23656          * @param {Roo.EventObject} e The raw event object
23657          */
23658             "contextmenu" : true,
23659         /**
23660          * @event selectionchange
23661          * Fires when the selected nodes change.
23662          * @param {Roo.View} this
23663          * @param {Array} selections Array of the selected nodes
23664          */
23665             "selectionchange" : true,
23666     
23667         /**
23668          * @event beforeselect
23669          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23670          * @param {Roo.View} this
23671          * @param {HTMLElement} node The node to be selected
23672          * @param {Array} selections Array of currently selected nodes
23673          */
23674             "beforeselect" : true,
23675         /**
23676          * @event preparedata
23677          * Fires on every row to render, to allow you to change the data.
23678          * @param {Roo.View} this
23679          * @param {Object} data to be rendered (change this)
23680          */
23681           "preparedata" : true
23682         });
23683
23684     this.el.on({
23685         "click": this.onClick,
23686         "dblclick": this.onDblClick,
23687         "contextmenu": this.onContextMenu,
23688         scope:this
23689     });
23690
23691     this.selections = [];
23692     this.nodes = [];
23693     this.cmp = new Roo.CompositeElementLite([]);
23694     if(this.store){
23695         this.store = Roo.factory(this.store, Roo.data);
23696         this.setStore(this.store, true);
23697     }
23698     Roo.View.superclass.constructor.call(this);
23699 };
23700
23701 Roo.extend(Roo.View, Roo.util.Observable, {
23702     
23703      /**
23704      * @cfg {Roo.data.Store} store Data store to load data from.
23705      */
23706     store : false,
23707     
23708     /**
23709      * @cfg {String|Roo.Element} el The container element.
23710      */
23711     el : '',
23712     
23713     /**
23714      * @cfg {String|Roo.Template} tpl The template used by this View 
23715      */
23716     tpl : false,
23717     
23718     /**
23719      * @cfg {String} selectedClass The css class to add to selected nodes
23720      */
23721     selectedClass : "x-view-selected",
23722      /**
23723      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23724      */
23725     emptyText : "",
23726     /**
23727      * @cfg {Boolean} multiSelect Allow multiple selection
23728      */
23729     multiSelect : false,
23730     /**
23731      * @cfg {Boolean} singleSelect Allow single selection
23732      */
23733     singleSelect:  false,
23734     
23735     /**
23736      * @cfg {Boolean} toggleSelect - selecting 
23737      */
23738     toggleSelect : false,
23739     
23740     /**
23741      * Returns the element this view is bound to.
23742      * @return {Roo.Element}
23743      */
23744     getEl : function(){
23745         return this.el;
23746     },
23747
23748     /**
23749      * Refreshes the view.
23750      */
23751     refresh : function(){
23752         var t = this.tpl;
23753         this.clearSelections();
23754         this.el.update("");
23755         var html = [];
23756         var records = this.store.getRange();
23757         if(records.length < 1){
23758             this.el.update(this.emptyText);
23759             return;
23760         }
23761         for(var i = 0, len = records.length; i < len; i++){
23762             var data = this.prepareData(records[i].data, i, records[i]);
23763             this.fireEvent("preparedata", this, data, i, records[i]);
23764             html[html.length] = t.apply(data);
23765         }
23766         this.el.update(html.join(""));
23767         this.nodes = this.el.dom.childNodes;
23768         this.updateIndexes(0);
23769     },
23770
23771     /**
23772      * Function to override to reformat the data that is sent to
23773      * the template for each node.
23774      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23775      * a JSON object for an UpdateManager bound view).
23776      */
23777     prepareData : function(data){
23778         return data;
23779     },
23780
23781     onUpdate : function(ds, record){
23782         this.clearSelections();
23783         var index = this.store.indexOf(record);
23784         var n = this.nodes[index];
23785         this.tpl.insertBefore(n, this.prepareData(record.data));
23786         n.parentNode.removeChild(n);
23787         this.updateIndexes(index, index);
23788     },
23789
23790     onAdd : function(ds, records, index){
23791         this.clearSelections();
23792         if(this.nodes.length == 0){
23793             this.refresh();
23794             return;
23795         }
23796         var n = this.nodes[index];
23797         for(var i = 0, len = records.length; i < len; i++){
23798             var d = this.prepareData(records[i].data);
23799             if(n){
23800                 this.tpl.insertBefore(n, d);
23801             }else{
23802                 this.tpl.append(this.el, d);
23803             }
23804         }
23805         this.updateIndexes(index);
23806     },
23807
23808     onRemove : function(ds, record, index){
23809         this.clearSelections();
23810         this.el.dom.removeChild(this.nodes[index]);
23811         this.updateIndexes(index);
23812     },
23813
23814     /**
23815      * Refresh an individual node.
23816      * @param {Number} index
23817      */
23818     refreshNode : function(index){
23819         this.onUpdate(this.store, this.store.getAt(index));
23820     },
23821
23822     updateIndexes : function(startIndex, endIndex){
23823         var ns = this.nodes;
23824         startIndex = startIndex || 0;
23825         endIndex = endIndex || ns.length - 1;
23826         for(var i = startIndex; i <= endIndex; i++){
23827             ns[i].nodeIndex = i;
23828         }
23829     },
23830
23831     /**
23832      * Changes the data store this view uses and refresh the view.
23833      * @param {Store} store
23834      */
23835     setStore : function(store, initial){
23836         if(!initial && this.store){
23837             this.store.un("datachanged", this.refresh);
23838             this.store.un("add", this.onAdd);
23839             this.store.un("remove", this.onRemove);
23840             this.store.un("update", this.onUpdate);
23841             this.store.un("clear", this.refresh);
23842         }
23843         if(store){
23844           
23845             store.on("datachanged", this.refresh, this);
23846             store.on("add", this.onAdd, this);
23847             store.on("remove", this.onRemove, this);
23848             store.on("update", this.onUpdate, this);
23849             store.on("clear", this.refresh, this);
23850         }
23851         
23852         if(store){
23853             this.refresh();
23854         }
23855     },
23856
23857     /**
23858      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23859      * @param {HTMLElement} node
23860      * @return {HTMLElement} The template node
23861      */
23862     findItemFromChild : function(node){
23863         var el = this.el.dom;
23864         if(!node || node.parentNode == el){
23865                     return node;
23866             }
23867             var p = node.parentNode;
23868             while(p && p != el){
23869             if(p.parentNode == el){
23870                 return p;
23871             }
23872             p = p.parentNode;
23873         }
23874             return null;
23875     },
23876
23877     /** @ignore */
23878     onClick : function(e){
23879         var item = this.findItemFromChild(e.getTarget());
23880         if(item){
23881             var index = this.indexOf(item);
23882             if(this.onItemClick(item, index, e) !== false){
23883                 this.fireEvent("click", this, index, item, e);
23884             }
23885         }else{
23886             this.clearSelections();
23887         }
23888     },
23889
23890     /** @ignore */
23891     onContextMenu : function(e){
23892         var item = this.findItemFromChild(e.getTarget());
23893         if(item){
23894             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23895         }
23896     },
23897
23898     /** @ignore */
23899     onDblClick : function(e){
23900         var item = this.findItemFromChild(e.getTarget());
23901         if(item){
23902             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23903         }
23904     },
23905
23906     onItemClick : function(item, index, e)
23907     {
23908         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23909             return false;
23910         }
23911         if (this.toggleSelect) {
23912             var m = this.isSelected(item) ? 'unselect' : 'select';
23913             Roo.log(m);
23914             var _t = this;
23915             _t[m](item, true, false);
23916             return true;
23917         }
23918         if(this.multiSelect || this.singleSelect){
23919             if(this.multiSelect && e.shiftKey && this.lastSelection){
23920                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23921             }else{
23922                 this.select(item, this.multiSelect && e.ctrlKey);
23923                 this.lastSelection = item;
23924             }
23925             e.preventDefault();
23926         }
23927         return true;
23928     },
23929
23930     /**
23931      * Get the number of selected nodes.
23932      * @return {Number}
23933      */
23934     getSelectionCount : function(){
23935         return this.selections.length;
23936     },
23937
23938     /**
23939      * Get the currently selected nodes.
23940      * @return {Array} An array of HTMLElements
23941      */
23942     getSelectedNodes : function(){
23943         return this.selections;
23944     },
23945
23946     /**
23947      * Get the indexes of the selected nodes.
23948      * @return {Array}
23949      */
23950     getSelectedIndexes : function(){
23951         var indexes = [], s = this.selections;
23952         for(var i = 0, len = s.length; i < len; i++){
23953             indexes.push(s[i].nodeIndex);
23954         }
23955         return indexes;
23956     },
23957
23958     /**
23959      * Clear all selections
23960      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23961      */
23962     clearSelections : function(suppressEvent){
23963         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23964             this.cmp.elements = this.selections;
23965             this.cmp.removeClass(this.selectedClass);
23966             this.selections = [];
23967             if(!suppressEvent){
23968                 this.fireEvent("selectionchange", this, this.selections);
23969             }
23970         }
23971     },
23972
23973     /**
23974      * Returns true if the passed node is selected
23975      * @param {HTMLElement/Number} node The node or node index
23976      * @return {Boolean}
23977      */
23978     isSelected : function(node){
23979         var s = this.selections;
23980         if(s.length < 1){
23981             return false;
23982         }
23983         node = this.getNode(node);
23984         return s.indexOf(node) !== -1;
23985     },
23986
23987     /**
23988      * Selects nodes.
23989      * @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
23990      * @param {Boolean} keepExisting (optional) true to keep existing selections
23991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23992      */
23993     select : function(nodeInfo, keepExisting, suppressEvent){
23994         if(nodeInfo instanceof Array){
23995             if(!keepExisting){
23996                 this.clearSelections(true);
23997             }
23998             for(var i = 0, len = nodeInfo.length; i < len; i++){
23999                 this.select(nodeInfo[i], true, true);
24000             }
24001             return;
24002         } 
24003         var node = this.getNode(nodeInfo);
24004         if(!node || this.isSelected(node)){
24005             return; // already selected.
24006         }
24007         if(!keepExisting){
24008             this.clearSelections(true);
24009         }
24010         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24011             Roo.fly(node).addClass(this.selectedClass);
24012             this.selections.push(node);
24013             if(!suppressEvent){
24014                 this.fireEvent("selectionchange", this, this.selections);
24015             }
24016         }
24017         
24018         
24019     },
24020       /**
24021      * Unselects nodes.
24022      * @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
24023      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24024      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24025      */
24026     unselect : function(nodeInfo, keepExisting, suppressEvent)
24027     {
24028         if(nodeInfo instanceof Array){
24029             Roo.each(this.selections, function(s) {
24030                 this.unselect(s, nodeInfo);
24031             }, this);
24032             return;
24033         }
24034         var node = this.getNode(nodeInfo);
24035         if(!node || !this.isSelected(node)){
24036             Roo.log("not selected");
24037             return; // not selected.
24038         }
24039         // fireevent???
24040         var ns = [];
24041         Roo.each(this.selections, function(s) {
24042             if (s == node ) {
24043                 Roo.fly(node).removeClass(this.selectedClass);
24044
24045                 return;
24046             }
24047             ns.push(s);
24048         },this);
24049         
24050         this.selections= ns;
24051         this.fireEvent("selectionchange", this, this.selections);
24052     },
24053
24054     /**
24055      * Gets a template node.
24056      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24057      * @return {HTMLElement} The node or null if it wasn't found
24058      */
24059     getNode : function(nodeInfo){
24060         if(typeof nodeInfo == "string"){
24061             return document.getElementById(nodeInfo);
24062         }else if(typeof nodeInfo == "number"){
24063             return this.nodes[nodeInfo];
24064         }
24065         return nodeInfo;
24066     },
24067
24068     /**
24069      * Gets a range template nodes.
24070      * @param {Number} startIndex
24071      * @param {Number} endIndex
24072      * @return {Array} An array of nodes
24073      */
24074     getNodes : function(start, end){
24075         var ns = this.nodes;
24076         start = start || 0;
24077         end = typeof end == "undefined" ? ns.length - 1 : end;
24078         var nodes = [];
24079         if(start <= end){
24080             for(var i = start; i <= end; i++){
24081                 nodes.push(ns[i]);
24082             }
24083         } else{
24084             for(var i = start; i >= end; i--){
24085                 nodes.push(ns[i]);
24086             }
24087         }
24088         return nodes;
24089     },
24090
24091     /**
24092      * Finds the index of the passed node
24093      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24094      * @return {Number} The index of the node or -1
24095      */
24096     indexOf : function(node){
24097         node = this.getNode(node);
24098         if(typeof node.nodeIndex == "number"){
24099             return node.nodeIndex;
24100         }
24101         var ns = this.nodes;
24102         for(var i = 0, len = ns.length; i < len; i++){
24103             if(ns[i] == node){
24104                 return i;
24105             }
24106         }
24107         return -1;
24108     }
24109 });
24110 /*
24111  * Based on:
24112  * Ext JS Library 1.1.1
24113  * Copyright(c) 2006-2007, Ext JS, LLC.
24114  *
24115  * Originally Released Under LGPL - original licence link has changed is not relivant.
24116  *
24117  * Fork - LGPL
24118  * <script type="text/javascript">
24119  */
24120
24121 /**
24122  * @class Roo.JsonView
24123  * @extends Roo.View
24124  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24125 <pre><code>
24126 var view = new Roo.JsonView({
24127     container: "my-element",
24128     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24129     multiSelect: true, 
24130     jsonRoot: "data" 
24131 });
24132
24133 // listen for node click?
24134 view.on("click", function(vw, index, node, e){
24135     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24136 });
24137
24138 // direct load of JSON data
24139 view.load("foobar.php");
24140
24141 // Example from my blog list
24142 var tpl = new Roo.Template(
24143     '&lt;div class="entry"&gt;' +
24144     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24145     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24146     "&lt;/div&gt;&lt;hr /&gt;"
24147 );
24148
24149 var moreView = new Roo.JsonView({
24150     container :  "entry-list", 
24151     template : tpl,
24152     jsonRoot: "posts"
24153 });
24154 moreView.on("beforerender", this.sortEntries, this);
24155 moreView.load({
24156     url: "/blog/get-posts.php",
24157     params: "allposts=true",
24158     text: "Loading Blog Entries..."
24159 });
24160 </code></pre>
24161
24162 * Note: old code is supported with arguments : (container, template, config)
24163
24164
24165  * @constructor
24166  * Create a new JsonView
24167  * 
24168  * @param {Object} config The config object
24169  * 
24170  */
24171 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24172     
24173     
24174     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24175
24176     var um = this.el.getUpdateManager();
24177     um.setRenderer(this);
24178     um.on("update", this.onLoad, this);
24179     um.on("failure", this.onLoadException, this);
24180
24181     /**
24182      * @event beforerender
24183      * Fires before rendering of the downloaded JSON data.
24184      * @param {Roo.JsonView} this
24185      * @param {Object} data The JSON data loaded
24186      */
24187     /**
24188      * @event load
24189      * Fires when data is loaded.
24190      * @param {Roo.JsonView} this
24191      * @param {Object} data The JSON data loaded
24192      * @param {Object} response The raw Connect response object
24193      */
24194     /**
24195      * @event loadexception
24196      * Fires when loading fails.
24197      * @param {Roo.JsonView} this
24198      * @param {Object} response The raw Connect response object
24199      */
24200     this.addEvents({
24201         'beforerender' : true,
24202         'load' : true,
24203         'loadexception' : true
24204     });
24205 };
24206 Roo.extend(Roo.JsonView, Roo.View, {
24207     /**
24208      * @type {String} The root property in the loaded JSON object that contains the data
24209      */
24210     jsonRoot : "",
24211
24212     /**
24213      * Refreshes the view.
24214      */
24215     refresh : function(){
24216         this.clearSelections();
24217         this.el.update("");
24218         var html = [];
24219         var o = this.jsonData;
24220         if(o && o.length > 0){
24221             for(var i = 0, len = o.length; i < len; i++){
24222                 var data = this.prepareData(o[i], i, o);
24223                 html[html.length] = this.tpl.apply(data);
24224             }
24225         }else{
24226             html.push(this.emptyText);
24227         }
24228         this.el.update(html.join(""));
24229         this.nodes = this.el.dom.childNodes;
24230         this.updateIndexes(0);
24231     },
24232
24233     /**
24234      * 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.
24235      * @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:
24236      <pre><code>
24237      view.load({
24238          url: "your-url.php",
24239          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24240          callback: yourFunction,
24241          scope: yourObject, //(optional scope)
24242          discardUrl: false,
24243          nocache: false,
24244          text: "Loading...",
24245          timeout: 30,
24246          scripts: false
24247      });
24248      </code></pre>
24249      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24250      * 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.
24251      * @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}
24252      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24253      * @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.
24254      */
24255     load : function(){
24256         var um = this.el.getUpdateManager();
24257         um.update.apply(um, arguments);
24258     },
24259
24260     render : function(el, response){
24261         this.clearSelections();
24262         this.el.update("");
24263         var o;
24264         try{
24265             o = Roo.util.JSON.decode(response.responseText);
24266             if(this.jsonRoot){
24267                 
24268                 o = o[this.jsonRoot];
24269             }
24270         } catch(e){
24271         }
24272         /**
24273          * The current JSON data or null
24274          */
24275         this.jsonData = o;
24276         this.beforeRender();
24277         this.refresh();
24278     },
24279
24280 /**
24281  * Get the number of records in the current JSON dataset
24282  * @return {Number}
24283  */
24284     getCount : function(){
24285         return this.jsonData ? this.jsonData.length : 0;
24286     },
24287
24288 /**
24289  * Returns the JSON object for the specified node(s)
24290  * @param {HTMLElement/Array} node The node or an array of nodes
24291  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24292  * you get the JSON object for the node
24293  */
24294     getNodeData : function(node){
24295         if(node instanceof Array){
24296             var data = [];
24297             for(var i = 0, len = node.length; i < len; i++){
24298                 data.push(this.getNodeData(node[i]));
24299             }
24300             return data;
24301         }
24302         return this.jsonData[this.indexOf(node)] || null;
24303     },
24304
24305     beforeRender : function(){
24306         this.snapshot = this.jsonData;
24307         if(this.sortInfo){
24308             this.sort.apply(this, this.sortInfo);
24309         }
24310         this.fireEvent("beforerender", this, this.jsonData);
24311     },
24312
24313     onLoad : function(el, o){
24314         this.fireEvent("load", this, this.jsonData, o);
24315     },
24316
24317     onLoadException : function(el, o){
24318         this.fireEvent("loadexception", this, o);
24319     },
24320
24321 /**
24322  * Filter the data by a specific property.
24323  * @param {String} property A property on your JSON objects
24324  * @param {String/RegExp} value Either string that the property values
24325  * should start with, or a RegExp to test against the property
24326  */
24327     filter : function(property, value){
24328         if(this.jsonData){
24329             var data = [];
24330             var ss = this.snapshot;
24331             if(typeof value == "string"){
24332                 var vlen = value.length;
24333                 if(vlen == 0){
24334                     this.clearFilter();
24335                     return;
24336                 }
24337                 value = value.toLowerCase();
24338                 for(var i = 0, len = ss.length; i < len; i++){
24339                     var o = ss[i];
24340                     if(o[property].substr(0, vlen).toLowerCase() == value){
24341                         data.push(o);
24342                     }
24343                 }
24344             } else if(value.exec){ // regex?
24345                 for(var i = 0, len = ss.length; i < len; i++){
24346                     var o = ss[i];
24347                     if(value.test(o[property])){
24348                         data.push(o);
24349                     }
24350                 }
24351             } else{
24352                 return;
24353             }
24354             this.jsonData = data;
24355             this.refresh();
24356         }
24357     },
24358
24359 /**
24360  * Filter by a function. The passed function will be called with each
24361  * object in the current dataset. If the function returns true the value is kept,
24362  * otherwise it is filtered.
24363  * @param {Function} fn
24364  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24365  */
24366     filterBy : function(fn, scope){
24367         if(this.jsonData){
24368             var data = [];
24369             var ss = this.snapshot;
24370             for(var i = 0, len = ss.length; i < len; i++){
24371                 var o = ss[i];
24372                 if(fn.call(scope || this, o)){
24373                     data.push(o);
24374                 }
24375             }
24376             this.jsonData = data;
24377             this.refresh();
24378         }
24379     },
24380
24381 /**
24382  * Clears the current filter.
24383  */
24384     clearFilter : function(){
24385         if(this.snapshot && this.jsonData != this.snapshot){
24386             this.jsonData = this.snapshot;
24387             this.refresh();
24388         }
24389     },
24390
24391
24392 /**
24393  * Sorts the data for this view and refreshes it.
24394  * @param {String} property A property on your JSON objects to sort on
24395  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24396  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24397  */
24398     sort : function(property, dir, sortType){
24399         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24400         if(this.jsonData){
24401             var p = property;
24402             var dsc = dir && dir.toLowerCase() == "desc";
24403             var f = function(o1, o2){
24404                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24405                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24406                 ;
24407                 if(v1 < v2){
24408                     return dsc ? +1 : -1;
24409                 } else if(v1 > v2){
24410                     return dsc ? -1 : +1;
24411                 } else{
24412                     return 0;
24413                 }
24414             };
24415             this.jsonData.sort(f);
24416             this.refresh();
24417             if(this.jsonData != this.snapshot){
24418                 this.snapshot.sort(f);
24419             }
24420         }
24421     }
24422 });/*
24423  * Based on:
24424  * Ext JS Library 1.1.1
24425  * Copyright(c) 2006-2007, Ext JS, LLC.
24426  *
24427  * Originally Released Under LGPL - original licence link has changed is not relivant.
24428  *
24429  * Fork - LGPL
24430  * <script type="text/javascript">
24431  */
24432  
24433
24434 /**
24435  * @class Roo.ColorPalette
24436  * @extends Roo.Component
24437  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24438  * Here's an example of typical usage:
24439  * <pre><code>
24440 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24441 cp.render('my-div');
24442
24443 cp.on('select', function(palette, selColor){
24444     // do something with selColor
24445 });
24446 </code></pre>
24447  * @constructor
24448  * Create a new ColorPalette
24449  * @param {Object} config The config object
24450  */
24451 Roo.ColorPalette = function(config){
24452     Roo.ColorPalette.superclass.constructor.call(this, config);
24453     this.addEvents({
24454         /**
24455              * @event select
24456              * Fires when a color is selected
24457              * @param {ColorPalette} this
24458              * @param {String} color The 6-digit color hex code (without the # symbol)
24459              */
24460         select: true
24461     });
24462
24463     if(this.handler){
24464         this.on("select", this.handler, this.scope, true);
24465     }
24466 };
24467 Roo.extend(Roo.ColorPalette, Roo.Component, {
24468     /**
24469      * @cfg {String} itemCls
24470      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24471      */
24472     itemCls : "x-color-palette",
24473     /**
24474      * @cfg {String} value
24475      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24476      * the hex codes are case-sensitive.
24477      */
24478     value : null,
24479     clickEvent:'click',
24480     // private
24481     ctype: "Roo.ColorPalette",
24482
24483     /**
24484      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24485      */
24486     allowReselect : false,
24487
24488     /**
24489      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24490      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24491      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24492      * of colors with the width setting until the box is symmetrical.</p>
24493      * <p>You can override individual colors if needed:</p>
24494      * <pre><code>
24495 var cp = new Roo.ColorPalette();
24496 cp.colors[0] = "FF0000";  // change the first box to red
24497 </code></pre>
24498
24499 Or you can provide a custom array of your own for complete control:
24500 <pre><code>
24501 var cp = new Roo.ColorPalette();
24502 cp.colors = ["000000", "993300", "333300"];
24503 </code></pre>
24504      * @type Array
24505      */
24506     colors : [
24507         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24508         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24509         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24510         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24511         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24512     ],
24513
24514     // private
24515     onRender : function(container, position){
24516         var t = new Roo.MasterTemplate(
24517             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24518         );
24519         var c = this.colors;
24520         for(var i = 0, len = c.length; i < len; i++){
24521             t.add([c[i]]);
24522         }
24523         var el = document.createElement("div");
24524         el.className = this.itemCls;
24525         t.overwrite(el);
24526         container.dom.insertBefore(el, position);
24527         this.el = Roo.get(el);
24528         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24529         if(this.clickEvent != 'click'){
24530             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24531         }
24532     },
24533
24534     // private
24535     afterRender : function(){
24536         Roo.ColorPalette.superclass.afterRender.call(this);
24537         if(this.value){
24538             var s = this.value;
24539             this.value = null;
24540             this.select(s);
24541         }
24542     },
24543
24544     // private
24545     handleClick : function(e, t){
24546         e.preventDefault();
24547         if(!this.disabled){
24548             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24549             this.select(c.toUpperCase());
24550         }
24551     },
24552
24553     /**
24554      * Selects the specified color in the palette (fires the select event)
24555      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24556      */
24557     select : function(color){
24558         color = color.replace("#", "");
24559         if(color != this.value || this.allowReselect){
24560             var el = this.el;
24561             if(this.value){
24562                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24563             }
24564             el.child("a.color-"+color).addClass("x-color-palette-sel");
24565             this.value = color;
24566             this.fireEvent("select", this, color);
24567         }
24568     }
24569 });/*
24570  * Based on:
24571  * Ext JS Library 1.1.1
24572  * Copyright(c) 2006-2007, Ext JS, LLC.
24573  *
24574  * Originally Released Under LGPL - original licence link has changed is not relivant.
24575  *
24576  * Fork - LGPL
24577  * <script type="text/javascript">
24578  */
24579  
24580 /**
24581  * @class Roo.DatePicker
24582  * @extends Roo.Component
24583  * Simple date picker class.
24584  * @constructor
24585  * Create a new DatePicker
24586  * @param {Object} config The config object
24587  */
24588 Roo.DatePicker = function(config){
24589     Roo.DatePicker.superclass.constructor.call(this, config);
24590
24591     this.value = config && config.value ?
24592                  config.value.clearTime() : new Date().clearTime();
24593
24594     this.addEvents({
24595         /**
24596              * @event select
24597              * Fires when a date is selected
24598              * @param {DatePicker} this
24599              * @param {Date} date The selected date
24600              */
24601         'select': true,
24602         /**
24603              * @event monthchange
24604              * Fires when the displayed month changes 
24605              * @param {DatePicker} this
24606              * @param {Date} date The selected month
24607              */
24608         'monthchange': true
24609     });
24610
24611     if(this.handler){
24612         this.on("select", this.handler,  this.scope || this);
24613     }
24614     // build the disabledDatesRE
24615     if(!this.disabledDatesRE && this.disabledDates){
24616         var dd = this.disabledDates;
24617         var re = "(?:";
24618         for(var i = 0; i < dd.length; i++){
24619             re += dd[i];
24620             if(i != dd.length-1) re += "|";
24621         }
24622         this.disabledDatesRE = new RegExp(re + ")");
24623     }
24624 };
24625
24626 Roo.extend(Roo.DatePicker, Roo.Component, {
24627     /**
24628      * @cfg {String} todayText
24629      * The text to display on the button that selects the current date (defaults to "Today")
24630      */
24631     todayText : "Today",
24632     /**
24633      * @cfg {String} okText
24634      * The text to display on the ok button
24635      */
24636     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24637     /**
24638      * @cfg {String} cancelText
24639      * The text to display on the cancel button
24640      */
24641     cancelText : "Cancel",
24642     /**
24643      * @cfg {String} todayTip
24644      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24645      */
24646     todayTip : "{0} (Spacebar)",
24647     /**
24648      * @cfg {Date} minDate
24649      * Minimum allowable date (JavaScript date object, defaults to null)
24650      */
24651     minDate : null,
24652     /**
24653      * @cfg {Date} maxDate
24654      * Maximum allowable date (JavaScript date object, defaults to null)
24655      */
24656     maxDate : null,
24657     /**
24658      * @cfg {String} minText
24659      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24660      */
24661     minText : "This date is before the minimum date",
24662     /**
24663      * @cfg {String} maxText
24664      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24665      */
24666     maxText : "This date is after the maximum date",
24667     /**
24668      * @cfg {String} format
24669      * The default date format string which can be overriden for localization support.  The format must be
24670      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24671      */
24672     format : "m/d/y",
24673     /**
24674      * @cfg {Array} disabledDays
24675      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24676      */
24677     disabledDays : null,
24678     /**
24679      * @cfg {String} disabledDaysText
24680      * The tooltip to display when the date falls on a disabled day (defaults to "")
24681      */
24682     disabledDaysText : "",
24683     /**
24684      * @cfg {RegExp} disabledDatesRE
24685      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24686      */
24687     disabledDatesRE : null,
24688     /**
24689      * @cfg {String} disabledDatesText
24690      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24691      */
24692     disabledDatesText : "",
24693     /**
24694      * @cfg {Boolean} constrainToViewport
24695      * True to constrain the date picker to the viewport (defaults to true)
24696      */
24697     constrainToViewport : true,
24698     /**
24699      * @cfg {Array} monthNames
24700      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24701      */
24702     monthNames : Date.monthNames,
24703     /**
24704      * @cfg {Array} dayNames
24705      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24706      */
24707     dayNames : Date.dayNames,
24708     /**
24709      * @cfg {String} nextText
24710      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24711      */
24712     nextText: 'Next Month (Control+Right)',
24713     /**
24714      * @cfg {String} prevText
24715      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24716      */
24717     prevText: 'Previous Month (Control+Left)',
24718     /**
24719      * @cfg {String} monthYearText
24720      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24721      */
24722     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24723     /**
24724      * @cfg {Number} startDay
24725      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24726      */
24727     startDay : 0,
24728     /**
24729      * @cfg {Bool} showClear
24730      * Show a clear button (usefull for date form elements that can be blank.)
24731      */
24732     
24733     showClear: false,
24734     
24735     /**
24736      * Sets the value of the date field
24737      * @param {Date} value The date to set
24738      */
24739     setValue : function(value){
24740         var old = this.value;
24741         this.value = value.clearTime(true);
24742         if(this.el){
24743             this.update(this.value);
24744         }
24745     },
24746
24747     /**
24748      * Gets the current selected value of the date field
24749      * @return {Date} The selected date
24750      */
24751     getValue : function(){
24752         return this.value;
24753     },
24754
24755     // private
24756     focus : function(){
24757         if(this.el){
24758             this.update(this.activeDate);
24759         }
24760     },
24761
24762     // private
24763     onRender : function(container, position){
24764         var m = [
24765              '<table cellspacing="0">',
24766                 '<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>',
24767                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24768         var dn = this.dayNames;
24769         for(var i = 0; i < 7; i++){
24770             var d = this.startDay+i;
24771             if(d > 6){
24772                 d = d-7;
24773             }
24774             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24775         }
24776         m[m.length] = "</tr></thead><tbody><tr>";
24777         for(var i = 0; i < 42; i++) {
24778             if(i % 7 == 0 && i != 0){
24779                 m[m.length] = "</tr><tr>";
24780             }
24781             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24782         }
24783         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24784             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24785
24786         var el = document.createElement("div");
24787         el.className = "x-date-picker";
24788         el.innerHTML = m.join("");
24789
24790         container.dom.insertBefore(el, position);
24791
24792         this.el = Roo.get(el);
24793         this.eventEl = Roo.get(el.firstChild);
24794
24795         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24796             handler: this.showPrevMonth,
24797             scope: this,
24798             preventDefault:true,
24799             stopDefault:true
24800         });
24801
24802         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24803             handler: this.showNextMonth,
24804             scope: this,
24805             preventDefault:true,
24806             stopDefault:true
24807         });
24808
24809         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24810
24811         this.monthPicker = this.el.down('div.x-date-mp');
24812         this.monthPicker.enableDisplayMode('block');
24813         
24814         var kn = new Roo.KeyNav(this.eventEl, {
24815             "left" : function(e){
24816                 e.ctrlKey ?
24817                     this.showPrevMonth() :
24818                     this.update(this.activeDate.add("d", -1));
24819             },
24820
24821             "right" : function(e){
24822                 e.ctrlKey ?
24823                     this.showNextMonth() :
24824                     this.update(this.activeDate.add("d", 1));
24825             },
24826
24827             "up" : function(e){
24828                 e.ctrlKey ?
24829                     this.showNextYear() :
24830                     this.update(this.activeDate.add("d", -7));
24831             },
24832
24833             "down" : function(e){
24834                 e.ctrlKey ?
24835                     this.showPrevYear() :
24836                     this.update(this.activeDate.add("d", 7));
24837             },
24838
24839             "pageUp" : function(e){
24840                 this.showNextMonth();
24841             },
24842
24843             "pageDown" : function(e){
24844                 this.showPrevMonth();
24845             },
24846
24847             "enter" : function(e){
24848                 e.stopPropagation();
24849                 return true;
24850             },
24851
24852             scope : this
24853         });
24854
24855         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24856
24857         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24858
24859         this.el.unselectable();
24860         
24861         this.cells = this.el.select("table.x-date-inner tbody td");
24862         this.textNodes = this.el.query("table.x-date-inner tbody span");
24863
24864         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24865             text: "&#160;",
24866             tooltip: this.monthYearText
24867         });
24868
24869         this.mbtn.on('click', this.showMonthPicker, this);
24870         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24871
24872
24873         var today = (new Date()).dateFormat(this.format);
24874         
24875         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24876         if (this.showClear) {
24877             baseTb.add( new Roo.Toolbar.Fill());
24878         }
24879         baseTb.add({
24880             text: String.format(this.todayText, today),
24881             tooltip: String.format(this.todayTip, today),
24882             handler: this.selectToday,
24883             scope: this
24884         });
24885         
24886         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24887             
24888         //});
24889         if (this.showClear) {
24890             
24891             baseTb.add( new Roo.Toolbar.Fill());
24892             baseTb.add({
24893                 text: '&#160;',
24894                 cls: 'x-btn-icon x-btn-clear',
24895                 handler: function() {
24896                     //this.value = '';
24897                     this.fireEvent("select", this, '');
24898                 },
24899                 scope: this
24900             });
24901         }
24902         
24903         
24904         if(Roo.isIE){
24905             this.el.repaint();
24906         }
24907         this.update(this.value);
24908     },
24909
24910     createMonthPicker : function(){
24911         if(!this.monthPicker.dom.firstChild){
24912             var buf = ['<table border="0" cellspacing="0">'];
24913             for(var i = 0; i < 6; i++){
24914                 buf.push(
24915                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24916                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24917                     i == 0 ?
24918                     '<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>' :
24919                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24920                 );
24921             }
24922             buf.push(
24923                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24924                     this.okText,
24925                     '</button><button type="button" class="x-date-mp-cancel">',
24926                     this.cancelText,
24927                     '</button></td></tr>',
24928                 '</table>'
24929             );
24930             this.monthPicker.update(buf.join(''));
24931             this.monthPicker.on('click', this.onMonthClick, this);
24932             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24933
24934             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24935             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24936
24937             this.mpMonths.each(function(m, a, i){
24938                 i += 1;
24939                 if((i%2) == 0){
24940                     m.dom.xmonth = 5 + Math.round(i * .5);
24941                 }else{
24942                     m.dom.xmonth = Math.round((i-1) * .5);
24943                 }
24944             });
24945         }
24946     },
24947
24948     showMonthPicker : function(){
24949         this.createMonthPicker();
24950         var size = this.el.getSize();
24951         this.monthPicker.setSize(size);
24952         this.monthPicker.child('table').setSize(size);
24953
24954         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24955         this.updateMPMonth(this.mpSelMonth);
24956         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24957         this.updateMPYear(this.mpSelYear);
24958
24959         this.monthPicker.slideIn('t', {duration:.2});
24960     },
24961
24962     updateMPYear : function(y){
24963         this.mpyear = y;
24964         var ys = this.mpYears.elements;
24965         for(var i = 1; i <= 10; i++){
24966             var td = ys[i-1], y2;
24967             if((i%2) == 0){
24968                 y2 = y + Math.round(i * .5);
24969                 td.firstChild.innerHTML = y2;
24970                 td.xyear = y2;
24971             }else{
24972                 y2 = y - (5-Math.round(i * .5));
24973                 td.firstChild.innerHTML = y2;
24974                 td.xyear = y2;
24975             }
24976             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24977         }
24978     },
24979
24980     updateMPMonth : function(sm){
24981         this.mpMonths.each(function(m, a, i){
24982             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24983         });
24984     },
24985
24986     selectMPMonth: function(m){
24987         
24988     },
24989
24990     onMonthClick : function(e, t){
24991         e.stopEvent();
24992         var el = new Roo.Element(t), pn;
24993         if(el.is('button.x-date-mp-cancel')){
24994             this.hideMonthPicker();
24995         }
24996         else if(el.is('button.x-date-mp-ok')){
24997             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24998             this.hideMonthPicker();
24999         }
25000         else if(pn = el.up('td.x-date-mp-month', 2)){
25001             this.mpMonths.removeClass('x-date-mp-sel');
25002             pn.addClass('x-date-mp-sel');
25003             this.mpSelMonth = pn.dom.xmonth;
25004         }
25005         else if(pn = el.up('td.x-date-mp-year', 2)){
25006             this.mpYears.removeClass('x-date-mp-sel');
25007             pn.addClass('x-date-mp-sel');
25008             this.mpSelYear = pn.dom.xyear;
25009         }
25010         else if(el.is('a.x-date-mp-prev')){
25011             this.updateMPYear(this.mpyear-10);
25012         }
25013         else if(el.is('a.x-date-mp-next')){
25014             this.updateMPYear(this.mpyear+10);
25015         }
25016     },
25017
25018     onMonthDblClick : function(e, t){
25019         e.stopEvent();
25020         var el = new Roo.Element(t), pn;
25021         if(pn = el.up('td.x-date-mp-month', 2)){
25022             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25023             this.hideMonthPicker();
25024         }
25025         else if(pn = el.up('td.x-date-mp-year', 2)){
25026             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25027             this.hideMonthPicker();
25028         }
25029     },
25030
25031     hideMonthPicker : function(disableAnim){
25032         if(this.monthPicker){
25033             if(disableAnim === true){
25034                 this.monthPicker.hide();
25035             }else{
25036                 this.monthPicker.slideOut('t', {duration:.2});
25037             }
25038         }
25039     },
25040
25041     // private
25042     showPrevMonth : function(e){
25043         this.update(this.activeDate.add("mo", -1));
25044     },
25045
25046     // private
25047     showNextMonth : function(e){
25048         this.update(this.activeDate.add("mo", 1));
25049     },
25050
25051     // private
25052     showPrevYear : function(){
25053         this.update(this.activeDate.add("y", -1));
25054     },
25055
25056     // private
25057     showNextYear : function(){
25058         this.update(this.activeDate.add("y", 1));
25059     },
25060
25061     // private
25062     handleMouseWheel : function(e){
25063         var delta = e.getWheelDelta();
25064         if(delta > 0){
25065             this.showPrevMonth();
25066             e.stopEvent();
25067         } else if(delta < 0){
25068             this.showNextMonth();
25069             e.stopEvent();
25070         }
25071     },
25072
25073     // private
25074     handleDateClick : function(e, t){
25075         e.stopEvent();
25076         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25077             this.setValue(new Date(t.dateValue));
25078             this.fireEvent("select", this, this.value);
25079         }
25080     },
25081
25082     // private
25083     selectToday : function(){
25084         this.setValue(new Date().clearTime());
25085         this.fireEvent("select", this, this.value);
25086     },
25087
25088     // private
25089     update : function(date)
25090     {
25091         var vd = this.activeDate;
25092         this.activeDate = date;
25093         if(vd && this.el){
25094             var t = date.getTime();
25095             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25096                 this.cells.removeClass("x-date-selected");
25097                 this.cells.each(function(c){
25098                    if(c.dom.firstChild.dateValue == t){
25099                        c.addClass("x-date-selected");
25100                        setTimeout(function(){
25101                             try{c.dom.firstChild.focus();}catch(e){}
25102                        }, 50);
25103                        return false;
25104                    }
25105                 });
25106                 return;
25107             }
25108         }
25109         
25110         var days = date.getDaysInMonth();
25111         var firstOfMonth = date.getFirstDateOfMonth();
25112         var startingPos = firstOfMonth.getDay()-this.startDay;
25113
25114         if(startingPos <= this.startDay){
25115             startingPos += 7;
25116         }
25117
25118         var pm = date.add("mo", -1);
25119         var prevStart = pm.getDaysInMonth()-startingPos;
25120
25121         var cells = this.cells.elements;
25122         var textEls = this.textNodes;
25123         days += startingPos;
25124
25125         // convert everything to numbers so it's fast
25126         var day = 86400000;
25127         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25128         var today = new Date().clearTime().getTime();
25129         var sel = date.clearTime().getTime();
25130         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25131         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25132         var ddMatch = this.disabledDatesRE;
25133         var ddText = this.disabledDatesText;
25134         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25135         var ddaysText = this.disabledDaysText;
25136         var format = this.format;
25137
25138         var setCellClass = function(cal, cell){
25139             cell.title = "";
25140             var t = d.getTime();
25141             cell.firstChild.dateValue = t;
25142             if(t == today){
25143                 cell.className += " x-date-today";
25144                 cell.title = cal.todayText;
25145             }
25146             if(t == sel){
25147                 cell.className += " x-date-selected";
25148                 setTimeout(function(){
25149                     try{cell.firstChild.focus();}catch(e){}
25150                 }, 50);
25151             }
25152             // disabling
25153             if(t < min) {
25154                 cell.className = " x-date-disabled";
25155                 cell.title = cal.minText;
25156                 return;
25157             }
25158             if(t > max) {
25159                 cell.className = " x-date-disabled";
25160                 cell.title = cal.maxText;
25161                 return;
25162             }
25163             if(ddays){
25164                 if(ddays.indexOf(d.getDay()) != -1){
25165                     cell.title = ddaysText;
25166                     cell.className = " x-date-disabled";
25167                 }
25168             }
25169             if(ddMatch && format){
25170                 var fvalue = d.dateFormat(format);
25171                 if(ddMatch.test(fvalue)){
25172                     cell.title = ddText.replace("%0", fvalue);
25173                     cell.className = " x-date-disabled";
25174                 }
25175             }
25176         };
25177
25178         var i = 0;
25179         for(; i < startingPos; i++) {
25180             textEls[i].innerHTML = (++prevStart);
25181             d.setDate(d.getDate()+1);
25182             cells[i].className = "x-date-prevday";
25183             setCellClass(this, cells[i]);
25184         }
25185         for(; i < days; i++){
25186             intDay = i - startingPos + 1;
25187             textEls[i].innerHTML = (intDay);
25188             d.setDate(d.getDate()+1);
25189             cells[i].className = "x-date-active";
25190             setCellClass(this, cells[i]);
25191         }
25192         var extraDays = 0;
25193         for(; i < 42; i++) {
25194              textEls[i].innerHTML = (++extraDays);
25195              d.setDate(d.getDate()+1);
25196              cells[i].className = "x-date-nextday";
25197              setCellClass(this, cells[i]);
25198         }
25199
25200         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25201         this.fireEvent('monthchange', this, date);
25202         
25203         if(!this.internalRender){
25204             var main = this.el.dom.firstChild;
25205             var w = main.offsetWidth;
25206             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25207             Roo.fly(main).setWidth(w);
25208             this.internalRender = true;
25209             // opera does not respect the auto grow header center column
25210             // then, after it gets a width opera refuses to recalculate
25211             // without a second pass
25212             if(Roo.isOpera && !this.secondPass){
25213                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25214                 this.secondPass = true;
25215                 this.update.defer(10, this, [date]);
25216             }
25217         }
25218         
25219         
25220     }
25221 });        /*
25222  * Based on:
25223  * Ext JS Library 1.1.1
25224  * Copyright(c) 2006-2007, Ext JS, LLC.
25225  *
25226  * Originally Released Under LGPL - original licence link has changed is not relivant.
25227  *
25228  * Fork - LGPL
25229  * <script type="text/javascript">
25230  */
25231 /**
25232  * @class Roo.TabPanel
25233  * @extends Roo.util.Observable
25234  * A lightweight tab container.
25235  * <br><br>
25236  * Usage:
25237  * <pre><code>
25238 // basic tabs 1, built from existing content
25239 var tabs = new Roo.TabPanel("tabs1");
25240 tabs.addTab("script", "View Script");
25241 tabs.addTab("markup", "View Markup");
25242 tabs.activate("script");
25243
25244 // more advanced tabs, built from javascript
25245 var jtabs = new Roo.TabPanel("jtabs");
25246 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25247
25248 // set up the UpdateManager
25249 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25250 var updater = tab2.getUpdateManager();
25251 updater.setDefaultUrl("ajax1.htm");
25252 tab2.on('activate', updater.refresh, updater, true);
25253
25254 // Use setUrl for Ajax loading
25255 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25256 tab3.setUrl("ajax2.htm", null, true);
25257
25258 // Disabled tab
25259 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25260 tab4.disable();
25261
25262 jtabs.activate("jtabs-1");
25263  * </code></pre>
25264  * @constructor
25265  * Create a new TabPanel.
25266  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25267  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25268  */
25269 Roo.TabPanel = function(container, config){
25270     /**
25271     * The container element for this TabPanel.
25272     * @type Roo.Element
25273     */
25274     this.el = Roo.get(container, true);
25275     if(config){
25276         if(typeof config == "boolean"){
25277             this.tabPosition = config ? "bottom" : "top";
25278         }else{
25279             Roo.apply(this, config);
25280         }
25281     }
25282     if(this.tabPosition == "bottom"){
25283         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25284         this.el.addClass("x-tabs-bottom");
25285     }
25286     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25287     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25288     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25289     if(Roo.isIE){
25290         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25291     }
25292     if(this.tabPosition != "bottom"){
25293         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25294          * @type Roo.Element
25295          */
25296         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25297         this.el.addClass("x-tabs-top");
25298     }
25299     this.items = [];
25300
25301     this.bodyEl.setStyle("position", "relative");
25302
25303     this.active = null;
25304     this.activateDelegate = this.activate.createDelegate(this);
25305
25306     this.addEvents({
25307         /**
25308          * @event tabchange
25309          * Fires when the active tab changes
25310          * @param {Roo.TabPanel} this
25311          * @param {Roo.TabPanelItem} activePanel The new active tab
25312          */
25313         "tabchange": true,
25314         /**
25315          * @event beforetabchange
25316          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25317          * @param {Roo.TabPanel} this
25318          * @param {Object} e Set cancel to true on this object to cancel the tab change
25319          * @param {Roo.TabPanelItem} tab The tab being changed to
25320          */
25321         "beforetabchange" : true
25322     });
25323
25324     Roo.EventManager.onWindowResize(this.onResize, this);
25325     this.cpad = this.el.getPadding("lr");
25326     this.hiddenCount = 0;
25327
25328
25329     // toolbar on the tabbar support...
25330     if (this.toolbar) {
25331         var tcfg = this.toolbar;
25332         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25333         this.toolbar = new Roo.Toolbar(tcfg);
25334         if (Roo.isSafari) {
25335             var tbl = tcfg.container.child('table', true);
25336             tbl.setAttribute('width', '100%');
25337         }
25338         
25339     }
25340    
25341
25342
25343     Roo.TabPanel.superclass.constructor.call(this);
25344 };
25345
25346 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25347     /*
25348      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25349      */
25350     tabPosition : "top",
25351     /*
25352      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25353      */
25354     currentTabWidth : 0,
25355     /*
25356      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25357      */
25358     minTabWidth : 40,
25359     /*
25360      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25361      */
25362     maxTabWidth : 250,
25363     /*
25364      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25365      */
25366     preferredTabWidth : 175,
25367     /*
25368      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25369      */
25370     resizeTabs : false,
25371     /*
25372      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25373      */
25374     monitorResize : true,
25375     /*
25376      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25377      */
25378     toolbar : false,
25379
25380     /**
25381      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25382      * @param {String} id The id of the div to use <b>or create</b>
25383      * @param {String} text The text for the tab
25384      * @param {String} content (optional) Content to put in the TabPanelItem body
25385      * @param {Boolean} closable (optional) True to create a close icon on the tab
25386      * @return {Roo.TabPanelItem} The created TabPanelItem
25387      */
25388     addTab : function(id, text, content, closable){
25389         var item = new Roo.TabPanelItem(this, id, text, closable);
25390         this.addTabItem(item);
25391         if(content){
25392             item.setContent(content);
25393         }
25394         return item;
25395     },
25396
25397     /**
25398      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25399      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25400      * @return {Roo.TabPanelItem}
25401      */
25402     getTab : function(id){
25403         return this.items[id];
25404     },
25405
25406     /**
25407      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25408      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25409      */
25410     hideTab : function(id){
25411         var t = this.items[id];
25412         if(!t.isHidden()){
25413            t.setHidden(true);
25414            this.hiddenCount++;
25415            this.autoSizeTabs();
25416         }
25417     },
25418
25419     /**
25420      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25421      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25422      */
25423     unhideTab : function(id){
25424         var t = this.items[id];
25425         if(t.isHidden()){
25426            t.setHidden(false);
25427            this.hiddenCount--;
25428            this.autoSizeTabs();
25429         }
25430     },
25431
25432     /**
25433      * Adds an existing {@link Roo.TabPanelItem}.
25434      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25435      */
25436     addTabItem : function(item){
25437         this.items[item.id] = item;
25438         this.items.push(item);
25439         if(this.resizeTabs){
25440            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25441            this.autoSizeTabs();
25442         }else{
25443             item.autoSize();
25444         }
25445     },
25446
25447     /**
25448      * Removes a {@link Roo.TabPanelItem}.
25449      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25450      */
25451     removeTab : function(id){
25452         var items = this.items;
25453         var tab = items[id];
25454         if(!tab) { return; }
25455         var index = items.indexOf(tab);
25456         if(this.active == tab && items.length > 1){
25457             var newTab = this.getNextAvailable(index);
25458             if(newTab) {
25459                 newTab.activate();
25460             }
25461         }
25462         this.stripEl.dom.removeChild(tab.pnode.dom);
25463         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25464             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25465         }
25466         items.splice(index, 1);
25467         delete this.items[tab.id];
25468         tab.fireEvent("close", tab);
25469         tab.purgeListeners();
25470         this.autoSizeTabs();
25471     },
25472
25473     getNextAvailable : function(start){
25474         var items = this.items;
25475         var index = start;
25476         // look for a next tab that will slide over to
25477         // replace the one being removed
25478         while(index < items.length){
25479             var item = items[++index];
25480             if(item && !item.isHidden()){
25481                 return item;
25482             }
25483         }
25484         // if one isn't found select the previous tab (on the left)
25485         index = start;
25486         while(index >= 0){
25487             var item = items[--index];
25488             if(item && !item.isHidden()){
25489                 return item;
25490             }
25491         }
25492         return null;
25493     },
25494
25495     /**
25496      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25497      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25498      */
25499     disableTab : function(id){
25500         var tab = this.items[id];
25501         if(tab && this.active != tab){
25502             tab.disable();
25503         }
25504     },
25505
25506     /**
25507      * Enables a {@link Roo.TabPanelItem} that is disabled.
25508      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25509      */
25510     enableTab : function(id){
25511         var tab = this.items[id];
25512         tab.enable();
25513     },
25514
25515     /**
25516      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25517      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25518      * @return {Roo.TabPanelItem} The TabPanelItem.
25519      */
25520     activate : function(id){
25521         var tab = this.items[id];
25522         if(!tab){
25523             return null;
25524         }
25525         if(tab == this.active || tab.disabled){
25526             return tab;
25527         }
25528         var e = {};
25529         this.fireEvent("beforetabchange", this, e, tab);
25530         if(e.cancel !== true && !tab.disabled){
25531             if(this.active){
25532                 this.active.hide();
25533             }
25534             this.active = this.items[id];
25535             this.active.show();
25536             this.fireEvent("tabchange", this, this.active);
25537         }
25538         return tab;
25539     },
25540
25541     /**
25542      * Gets the active {@link Roo.TabPanelItem}.
25543      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25544      */
25545     getActiveTab : function(){
25546         return this.active;
25547     },
25548
25549     /**
25550      * Updates the tab body element to fit the height of the container element
25551      * for overflow scrolling
25552      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25553      */
25554     syncHeight : function(targetHeight){
25555         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25556         var bm = this.bodyEl.getMargins();
25557         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25558         this.bodyEl.setHeight(newHeight);
25559         return newHeight;
25560     },
25561
25562     onResize : function(){
25563         if(this.monitorResize){
25564             this.autoSizeTabs();
25565         }
25566     },
25567
25568     /**
25569      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25570      */
25571     beginUpdate : function(){
25572         this.updating = true;
25573     },
25574
25575     /**
25576      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25577      */
25578     endUpdate : function(){
25579         this.updating = false;
25580         this.autoSizeTabs();
25581     },
25582
25583     /**
25584      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25585      */
25586     autoSizeTabs : function(){
25587         var count = this.items.length;
25588         var vcount = count - this.hiddenCount;
25589         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25590         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25591         var availWidth = Math.floor(w / vcount);
25592         var b = this.stripBody;
25593         if(b.getWidth() > w){
25594             var tabs = this.items;
25595             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25596             if(availWidth < this.minTabWidth){
25597                 /*if(!this.sleft){    // incomplete scrolling code
25598                     this.createScrollButtons();
25599                 }
25600                 this.showScroll();
25601                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25602             }
25603         }else{
25604             if(this.currentTabWidth < this.preferredTabWidth){
25605                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25606             }
25607         }
25608     },
25609
25610     /**
25611      * Returns the number of tabs in this TabPanel.
25612      * @return {Number}
25613      */
25614      getCount : function(){
25615          return this.items.length;
25616      },
25617
25618     /**
25619      * Resizes all the tabs to the passed width
25620      * @param {Number} The new width
25621      */
25622     setTabWidth : function(width){
25623         this.currentTabWidth = width;
25624         for(var i = 0, len = this.items.length; i < len; i++) {
25625                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25626         }
25627     },
25628
25629     /**
25630      * Destroys this TabPanel
25631      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25632      */
25633     destroy : function(removeEl){
25634         Roo.EventManager.removeResizeListener(this.onResize, this);
25635         for(var i = 0, len = this.items.length; i < len; i++){
25636             this.items[i].purgeListeners();
25637         }
25638         if(removeEl === true){
25639             this.el.update("");
25640             this.el.remove();
25641         }
25642     }
25643 });
25644
25645 /**
25646  * @class Roo.TabPanelItem
25647  * @extends Roo.util.Observable
25648  * Represents an individual item (tab plus body) in a TabPanel.
25649  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25650  * @param {String} id The id of this TabPanelItem
25651  * @param {String} text The text for the tab of this TabPanelItem
25652  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25653  */
25654 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25655     /**
25656      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25657      * @type Roo.TabPanel
25658      */
25659     this.tabPanel = tabPanel;
25660     /**
25661      * The id for this TabPanelItem
25662      * @type String
25663      */
25664     this.id = id;
25665     /** @private */
25666     this.disabled = false;
25667     /** @private */
25668     this.text = text;
25669     /** @private */
25670     this.loaded = false;
25671     this.closable = closable;
25672
25673     /**
25674      * The body element for this TabPanelItem.
25675      * @type Roo.Element
25676      */
25677     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25678     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25679     this.bodyEl.setStyle("display", "block");
25680     this.bodyEl.setStyle("zoom", "1");
25681     this.hideAction();
25682
25683     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25684     /** @private */
25685     this.el = Roo.get(els.el, true);
25686     this.inner = Roo.get(els.inner, true);
25687     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25688     this.pnode = Roo.get(els.el.parentNode, true);
25689     this.el.on("mousedown", this.onTabMouseDown, this);
25690     this.el.on("click", this.onTabClick, this);
25691     /** @private */
25692     if(closable){
25693         var c = Roo.get(els.close, true);
25694         c.dom.title = this.closeText;
25695         c.addClassOnOver("close-over");
25696         c.on("click", this.closeClick, this);
25697      }
25698
25699     this.addEvents({
25700          /**
25701          * @event activate
25702          * Fires when this tab becomes the active tab.
25703          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25704          * @param {Roo.TabPanelItem} this
25705          */
25706         "activate": true,
25707         /**
25708          * @event beforeclose
25709          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25710          * @param {Roo.TabPanelItem} this
25711          * @param {Object} e Set cancel to true on this object to cancel the close.
25712          */
25713         "beforeclose": true,
25714         /**
25715          * @event close
25716          * Fires when this tab is closed.
25717          * @param {Roo.TabPanelItem} this
25718          */
25719          "close": true,
25720         /**
25721          * @event deactivate
25722          * Fires when this tab is no longer the active tab.
25723          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25724          * @param {Roo.TabPanelItem} this
25725          */
25726          "deactivate" : true
25727     });
25728     this.hidden = false;
25729
25730     Roo.TabPanelItem.superclass.constructor.call(this);
25731 };
25732
25733 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25734     purgeListeners : function(){
25735        Roo.util.Observable.prototype.purgeListeners.call(this);
25736        this.el.removeAllListeners();
25737     },
25738     /**
25739      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25740      */
25741     show : function(){
25742         this.pnode.addClass("on");
25743         this.showAction();
25744         if(Roo.isOpera){
25745             this.tabPanel.stripWrap.repaint();
25746         }
25747         this.fireEvent("activate", this.tabPanel, this);
25748     },
25749
25750     /**
25751      * Returns true if this tab is the active tab.
25752      * @return {Boolean}
25753      */
25754     isActive : function(){
25755         return this.tabPanel.getActiveTab() == this;
25756     },
25757
25758     /**
25759      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25760      */
25761     hide : function(){
25762         this.pnode.removeClass("on");
25763         this.hideAction();
25764         this.fireEvent("deactivate", this.tabPanel, this);
25765     },
25766
25767     hideAction : function(){
25768         this.bodyEl.hide();
25769         this.bodyEl.setStyle("position", "absolute");
25770         this.bodyEl.setLeft("-20000px");
25771         this.bodyEl.setTop("-20000px");
25772     },
25773
25774     showAction : function(){
25775         this.bodyEl.setStyle("position", "relative");
25776         this.bodyEl.setTop("");
25777         this.bodyEl.setLeft("");
25778         this.bodyEl.show();
25779     },
25780
25781     /**
25782      * Set the tooltip for the tab.
25783      * @param {String} tooltip The tab's tooltip
25784      */
25785     setTooltip : function(text){
25786         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25787             this.textEl.dom.qtip = text;
25788             this.textEl.dom.removeAttribute('title');
25789         }else{
25790             this.textEl.dom.title = text;
25791         }
25792     },
25793
25794     onTabClick : function(e){
25795         e.preventDefault();
25796         this.tabPanel.activate(this.id);
25797     },
25798
25799     onTabMouseDown : function(e){
25800         e.preventDefault();
25801         this.tabPanel.activate(this.id);
25802     },
25803
25804     getWidth : function(){
25805         return this.inner.getWidth();
25806     },
25807
25808     setWidth : function(width){
25809         var iwidth = width - this.pnode.getPadding("lr");
25810         this.inner.setWidth(iwidth);
25811         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25812         this.pnode.setWidth(width);
25813     },
25814
25815     /**
25816      * Show or hide the tab
25817      * @param {Boolean} hidden True to hide or false to show.
25818      */
25819     setHidden : function(hidden){
25820         this.hidden = hidden;
25821         this.pnode.setStyle("display", hidden ? "none" : "");
25822     },
25823
25824     /**
25825      * Returns true if this tab is "hidden"
25826      * @return {Boolean}
25827      */
25828     isHidden : function(){
25829         return this.hidden;
25830     },
25831
25832     /**
25833      * Returns the text for this tab
25834      * @return {String}
25835      */
25836     getText : function(){
25837         return this.text;
25838     },
25839
25840     autoSize : function(){
25841         //this.el.beginMeasure();
25842         this.textEl.setWidth(1);
25843         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25844         //this.el.endMeasure();
25845     },
25846
25847     /**
25848      * Sets the text for the tab (Note: this also sets the tooltip text)
25849      * @param {String} text The tab's text and tooltip
25850      */
25851     setText : function(text){
25852         this.text = text;
25853         this.textEl.update(text);
25854         this.setTooltip(text);
25855         if(!this.tabPanel.resizeTabs){
25856             this.autoSize();
25857         }
25858     },
25859     /**
25860      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25861      */
25862     activate : function(){
25863         this.tabPanel.activate(this.id);
25864     },
25865
25866     /**
25867      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25868      */
25869     disable : function(){
25870         if(this.tabPanel.active != this){
25871             this.disabled = true;
25872             this.pnode.addClass("disabled");
25873         }
25874     },
25875
25876     /**
25877      * Enables this TabPanelItem if it was previously disabled.
25878      */
25879     enable : function(){
25880         this.disabled = false;
25881         this.pnode.removeClass("disabled");
25882     },
25883
25884     /**
25885      * Sets the content for this TabPanelItem.
25886      * @param {String} content The content
25887      * @param {Boolean} loadScripts true to look for and load scripts
25888      */
25889     setContent : function(content, loadScripts){
25890         this.bodyEl.update(content, loadScripts);
25891     },
25892
25893     /**
25894      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25895      * @return {Roo.UpdateManager} The UpdateManager
25896      */
25897     getUpdateManager : function(){
25898         return this.bodyEl.getUpdateManager();
25899     },
25900
25901     /**
25902      * Set a URL to be used to load the content for this TabPanelItem.
25903      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25904      * @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)
25905      * @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)
25906      * @return {Roo.UpdateManager} The UpdateManager
25907      */
25908     setUrl : function(url, params, loadOnce){
25909         if(this.refreshDelegate){
25910             this.un('activate', this.refreshDelegate);
25911         }
25912         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25913         this.on("activate", this.refreshDelegate);
25914         return this.bodyEl.getUpdateManager();
25915     },
25916
25917     /** @private */
25918     _handleRefresh : function(url, params, loadOnce){
25919         if(!loadOnce || !this.loaded){
25920             var updater = this.bodyEl.getUpdateManager();
25921             updater.update(url, params, this._setLoaded.createDelegate(this));
25922         }
25923     },
25924
25925     /**
25926      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25927      *   Will fail silently if the setUrl method has not been called.
25928      *   This does not activate the panel, just updates its content.
25929      */
25930     refresh : function(){
25931         if(this.refreshDelegate){
25932            this.loaded = false;
25933            this.refreshDelegate();
25934         }
25935     },
25936
25937     /** @private */
25938     _setLoaded : function(){
25939         this.loaded = true;
25940     },
25941
25942     /** @private */
25943     closeClick : function(e){
25944         var o = {};
25945         e.stopEvent();
25946         this.fireEvent("beforeclose", this, o);
25947         if(o.cancel !== true){
25948             this.tabPanel.removeTab(this.id);
25949         }
25950     },
25951     /**
25952      * The text displayed in the tooltip for the close icon.
25953      * @type String
25954      */
25955     closeText : "Close this tab"
25956 });
25957
25958 /** @private */
25959 Roo.TabPanel.prototype.createStrip = function(container){
25960     var strip = document.createElement("div");
25961     strip.className = "x-tabs-wrap";
25962     container.appendChild(strip);
25963     return strip;
25964 };
25965 /** @private */
25966 Roo.TabPanel.prototype.createStripList = function(strip){
25967     // div wrapper for retard IE
25968     // returns the "tr" element.
25969     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25970         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25971         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25972     return strip.firstChild.firstChild.firstChild.firstChild;
25973 };
25974 /** @private */
25975 Roo.TabPanel.prototype.createBody = function(container){
25976     var body = document.createElement("div");
25977     Roo.id(body, "tab-body");
25978     Roo.fly(body).addClass("x-tabs-body");
25979     container.appendChild(body);
25980     return body;
25981 };
25982 /** @private */
25983 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25984     var body = Roo.getDom(id);
25985     if(!body){
25986         body = document.createElement("div");
25987         body.id = id;
25988     }
25989     Roo.fly(body).addClass("x-tabs-item-body");
25990     bodyEl.insertBefore(body, bodyEl.firstChild);
25991     return body;
25992 };
25993 /** @private */
25994 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25995     var td = document.createElement("td");
25996     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
25997     //stripEl.appendChild(td);
25998     if(closable){
25999         td.className = "x-tabs-closable";
26000         if(!this.closeTpl){
26001             this.closeTpl = new Roo.Template(
26002                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26003                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26004                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26005             );
26006         }
26007         var el = this.closeTpl.overwrite(td, {"text": text});
26008         var close = el.getElementsByTagName("div")[0];
26009         var inner = el.getElementsByTagName("em")[0];
26010         return {"el": el, "close": close, "inner": inner};
26011     } else {
26012         if(!this.tabTpl){
26013             this.tabTpl = new Roo.Template(
26014                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26015                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26016             );
26017         }
26018         var el = this.tabTpl.overwrite(td, {"text": text});
26019         var inner = el.getElementsByTagName("em")[0];
26020         return {"el": el, "inner": inner};
26021     }
26022 };/*
26023  * Based on:
26024  * Ext JS Library 1.1.1
26025  * Copyright(c) 2006-2007, Ext JS, LLC.
26026  *
26027  * Originally Released Under LGPL - original licence link has changed is not relivant.
26028  *
26029  * Fork - LGPL
26030  * <script type="text/javascript">
26031  */
26032
26033 /**
26034  * @class Roo.Button
26035  * @extends Roo.util.Observable
26036  * Simple Button class
26037  * @cfg {String} text The button text
26038  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26039  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26040  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26041  * @cfg {Object} scope The scope of the handler
26042  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26043  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26044  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26045  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26046  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26047  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26048    applies if enableToggle = true)
26049  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26050  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26051   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26052  * @constructor
26053  * Create a new button
26054  * @param {Object} config The config object
26055  */
26056 Roo.Button = function(renderTo, config)
26057 {
26058     if (!config) {
26059         config = renderTo;
26060         renderTo = config.renderTo || false;
26061     }
26062     
26063     Roo.apply(this, config);
26064     this.addEvents({
26065         /**
26066              * @event click
26067              * Fires when this button is clicked
26068              * @param {Button} this
26069              * @param {EventObject} e The click event
26070              */
26071             "click" : true,
26072         /**
26073              * @event toggle
26074              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26075              * @param {Button} this
26076              * @param {Boolean} pressed
26077              */
26078             "toggle" : true,
26079         /**
26080              * @event mouseover
26081              * Fires when the mouse hovers over the button
26082              * @param {Button} this
26083              * @param {Event} e The event object
26084              */
26085         'mouseover' : true,
26086         /**
26087              * @event mouseout
26088              * Fires when the mouse exits the button
26089              * @param {Button} this
26090              * @param {Event} e The event object
26091              */
26092         'mouseout': true,
26093          /**
26094              * @event render
26095              * Fires when the button is rendered
26096              * @param {Button} this
26097              */
26098         'render': true
26099     });
26100     if(this.menu){
26101         this.menu = Roo.menu.MenuMgr.get(this.menu);
26102     }
26103     // register listeners first!!  - so render can be captured..
26104     Roo.util.Observable.call(this);
26105     if(renderTo){
26106         this.render(renderTo);
26107     }
26108     
26109   
26110 };
26111
26112 Roo.extend(Roo.Button, Roo.util.Observable, {
26113     /**
26114      * 
26115      */
26116     
26117     /**
26118      * Read-only. True if this button is hidden
26119      * @type Boolean
26120      */
26121     hidden : false,
26122     /**
26123      * Read-only. True if this button is disabled
26124      * @type Boolean
26125      */
26126     disabled : false,
26127     /**
26128      * Read-only. True if this button is pressed (only if enableToggle = true)
26129      * @type Boolean
26130      */
26131     pressed : false,
26132
26133     /**
26134      * @cfg {Number} tabIndex 
26135      * The DOM tabIndex for this button (defaults to undefined)
26136      */
26137     tabIndex : undefined,
26138
26139     /**
26140      * @cfg {Boolean} enableToggle
26141      * True to enable pressed/not pressed toggling (defaults to false)
26142      */
26143     enableToggle: false,
26144     /**
26145      * @cfg {Mixed} menu
26146      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26147      */
26148     menu : undefined,
26149     /**
26150      * @cfg {String} menuAlign
26151      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26152      */
26153     menuAlign : "tl-bl?",
26154
26155     /**
26156      * @cfg {String} iconCls
26157      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26158      */
26159     iconCls : undefined,
26160     /**
26161      * @cfg {String} type
26162      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26163      */
26164     type : 'button',
26165
26166     // private
26167     menuClassTarget: 'tr',
26168
26169     /**
26170      * @cfg {String} clickEvent
26171      * The type of event to map to the button's event handler (defaults to 'click')
26172      */
26173     clickEvent : 'click',
26174
26175     /**
26176      * @cfg {Boolean} handleMouseEvents
26177      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26178      */
26179     handleMouseEvents : true,
26180
26181     /**
26182      * @cfg {String} tooltipType
26183      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26184      */
26185     tooltipType : 'qtip',
26186
26187     /**
26188      * @cfg {String} cls
26189      * A CSS class to apply to the button's main element.
26190      */
26191     
26192     /**
26193      * @cfg {Roo.Template} template (Optional)
26194      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26195      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26196      * require code modifications if required elements (e.g. a button) aren't present.
26197      */
26198
26199     // private
26200     render : function(renderTo){
26201         var btn;
26202         if(this.hideParent){
26203             this.parentEl = Roo.get(renderTo);
26204         }
26205         if(!this.dhconfig){
26206             if(!this.template){
26207                 if(!Roo.Button.buttonTemplate){
26208                     // hideous table template
26209                     Roo.Button.buttonTemplate = new Roo.Template(
26210                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26211                         '<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>',
26212                         "</tr></tbody></table>");
26213                 }
26214                 this.template = Roo.Button.buttonTemplate;
26215             }
26216             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26217             var btnEl = btn.child("button:first");
26218             btnEl.on('focus', this.onFocus, this);
26219             btnEl.on('blur', this.onBlur, this);
26220             if(this.cls){
26221                 btn.addClass(this.cls);
26222             }
26223             if(this.icon){
26224                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26225             }
26226             if(this.iconCls){
26227                 btnEl.addClass(this.iconCls);
26228                 if(!this.cls){
26229                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26230                 }
26231             }
26232             if(this.tabIndex !== undefined){
26233                 btnEl.dom.tabIndex = this.tabIndex;
26234             }
26235             if(this.tooltip){
26236                 if(typeof this.tooltip == 'object'){
26237                     Roo.QuickTips.tips(Roo.apply({
26238                           target: btnEl.id
26239                     }, this.tooltip));
26240                 } else {
26241                     btnEl.dom[this.tooltipType] = this.tooltip;
26242                 }
26243             }
26244         }else{
26245             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26246         }
26247         this.el = btn;
26248         if(this.id){
26249             this.el.dom.id = this.el.id = this.id;
26250         }
26251         if(this.menu){
26252             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26253             this.menu.on("show", this.onMenuShow, this);
26254             this.menu.on("hide", this.onMenuHide, this);
26255         }
26256         btn.addClass("x-btn");
26257         if(Roo.isIE && !Roo.isIE7){
26258             this.autoWidth.defer(1, this);
26259         }else{
26260             this.autoWidth();
26261         }
26262         if(this.handleMouseEvents){
26263             btn.on("mouseover", this.onMouseOver, this);
26264             btn.on("mouseout", this.onMouseOut, this);
26265             btn.on("mousedown", this.onMouseDown, this);
26266         }
26267         btn.on(this.clickEvent, this.onClick, this);
26268         //btn.on("mouseup", this.onMouseUp, this);
26269         if(this.hidden){
26270             this.hide();
26271         }
26272         if(this.disabled){
26273             this.disable();
26274         }
26275         Roo.ButtonToggleMgr.register(this);
26276         if(this.pressed){
26277             this.el.addClass("x-btn-pressed");
26278         }
26279         if(this.repeat){
26280             var repeater = new Roo.util.ClickRepeater(btn,
26281                 typeof this.repeat == "object" ? this.repeat : {}
26282             );
26283             repeater.on("click", this.onClick,  this);
26284         }
26285         
26286         this.fireEvent('render', this);
26287         
26288     },
26289     /**
26290      * Returns the button's underlying element
26291      * @return {Roo.Element} The element
26292      */
26293     getEl : function(){
26294         return this.el;  
26295     },
26296     
26297     /**
26298      * Destroys this Button and removes any listeners.
26299      */
26300     destroy : function(){
26301         Roo.ButtonToggleMgr.unregister(this);
26302         this.el.removeAllListeners();
26303         this.purgeListeners();
26304         this.el.remove();
26305     },
26306
26307     // private
26308     autoWidth : function(){
26309         if(this.el){
26310             this.el.setWidth("auto");
26311             if(Roo.isIE7 && Roo.isStrict){
26312                 var ib = this.el.child('button');
26313                 if(ib && ib.getWidth() > 20){
26314                     ib.clip();
26315                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26316                 }
26317             }
26318             if(this.minWidth){
26319                 if(this.hidden){
26320                     this.el.beginMeasure();
26321                 }
26322                 if(this.el.getWidth() < this.minWidth){
26323                     this.el.setWidth(this.minWidth);
26324                 }
26325                 if(this.hidden){
26326                     this.el.endMeasure();
26327                 }
26328             }
26329         }
26330     },
26331
26332     /**
26333      * Assigns this button's click handler
26334      * @param {Function} handler The function to call when the button is clicked
26335      * @param {Object} scope (optional) Scope for the function passed in
26336      */
26337     setHandler : function(handler, scope){
26338         this.handler = handler;
26339         this.scope = scope;  
26340     },
26341     
26342     /**
26343      * Sets this button's text
26344      * @param {String} text The button text
26345      */
26346     setText : function(text){
26347         this.text = text;
26348         if(this.el){
26349             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26350         }
26351         this.autoWidth();
26352     },
26353     
26354     /**
26355      * Gets the text for this button
26356      * @return {String} The button text
26357      */
26358     getText : function(){
26359         return this.text;  
26360     },
26361     
26362     /**
26363      * Show this button
26364      */
26365     show: function(){
26366         this.hidden = false;
26367         if(this.el){
26368             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26369         }
26370     },
26371     
26372     /**
26373      * Hide this button
26374      */
26375     hide: function(){
26376         this.hidden = true;
26377         if(this.el){
26378             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26379         }
26380     },
26381     
26382     /**
26383      * Convenience function for boolean show/hide
26384      * @param {Boolean} visible True to show, false to hide
26385      */
26386     setVisible: function(visible){
26387         if(visible) {
26388             this.show();
26389         }else{
26390             this.hide();
26391         }
26392     },
26393     
26394     /**
26395      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26396      * @param {Boolean} state (optional) Force a particular state
26397      */
26398     toggle : function(state){
26399         state = state === undefined ? !this.pressed : state;
26400         if(state != this.pressed){
26401             if(state){
26402                 this.el.addClass("x-btn-pressed");
26403                 this.pressed = true;
26404                 this.fireEvent("toggle", this, true);
26405             }else{
26406                 this.el.removeClass("x-btn-pressed");
26407                 this.pressed = false;
26408                 this.fireEvent("toggle", this, false);
26409             }
26410             if(this.toggleHandler){
26411                 this.toggleHandler.call(this.scope || this, this, state);
26412             }
26413         }
26414     },
26415     
26416     /**
26417      * Focus the button
26418      */
26419     focus : function(){
26420         this.el.child('button:first').focus();
26421     },
26422     
26423     /**
26424      * Disable this button
26425      */
26426     disable : function(){
26427         if(this.el){
26428             this.el.addClass("x-btn-disabled");
26429         }
26430         this.disabled = true;
26431     },
26432     
26433     /**
26434      * Enable this button
26435      */
26436     enable : function(){
26437         if(this.el){
26438             this.el.removeClass("x-btn-disabled");
26439         }
26440         this.disabled = false;
26441     },
26442
26443     /**
26444      * Convenience function for boolean enable/disable
26445      * @param {Boolean} enabled True to enable, false to disable
26446      */
26447     setDisabled : function(v){
26448         this[v !== true ? "enable" : "disable"]();
26449     },
26450
26451     // private
26452     onClick : function(e){
26453         if(e){
26454             e.preventDefault();
26455         }
26456         if(e.button != 0){
26457             return;
26458         }
26459         if(!this.disabled){
26460             if(this.enableToggle){
26461                 this.toggle();
26462             }
26463             if(this.menu && !this.menu.isVisible()){
26464                 this.menu.show(this.el, this.menuAlign);
26465             }
26466             this.fireEvent("click", this, e);
26467             if(this.handler){
26468                 this.el.removeClass("x-btn-over");
26469                 this.handler.call(this.scope || this, this, e);
26470             }
26471         }
26472     },
26473     // private
26474     onMouseOver : function(e){
26475         if(!this.disabled){
26476             this.el.addClass("x-btn-over");
26477             this.fireEvent('mouseover', this, e);
26478         }
26479     },
26480     // private
26481     onMouseOut : function(e){
26482         if(!e.within(this.el,  true)){
26483             this.el.removeClass("x-btn-over");
26484             this.fireEvent('mouseout', this, e);
26485         }
26486     },
26487     // private
26488     onFocus : function(e){
26489         if(!this.disabled){
26490             this.el.addClass("x-btn-focus");
26491         }
26492     },
26493     // private
26494     onBlur : function(e){
26495         this.el.removeClass("x-btn-focus");
26496     },
26497     // private
26498     onMouseDown : function(e){
26499         if(!this.disabled && e.button == 0){
26500             this.el.addClass("x-btn-click");
26501             Roo.get(document).on('mouseup', this.onMouseUp, this);
26502         }
26503     },
26504     // private
26505     onMouseUp : function(e){
26506         if(e.button == 0){
26507             this.el.removeClass("x-btn-click");
26508             Roo.get(document).un('mouseup', this.onMouseUp, this);
26509         }
26510     },
26511     // private
26512     onMenuShow : function(e){
26513         this.el.addClass("x-btn-menu-active");
26514     },
26515     // private
26516     onMenuHide : function(e){
26517         this.el.removeClass("x-btn-menu-active");
26518     }   
26519 });
26520
26521 // Private utility class used by Button
26522 Roo.ButtonToggleMgr = function(){
26523    var groups = {};
26524    
26525    function toggleGroup(btn, state){
26526        if(state){
26527            var g = groups[btn.toggleGroup];
26528            for(var i = 0, l = g.length; i < l; i++){
26529                if(g[i] != btn){
26530                    g[i].toggle(false);
26531                }
26532            }
26533        }
26534    }
26535    
26536    return {
26537        register : function(btn){
26538            if(!btn.toggleGroup){
26539                return;
26540            }
26541            var g = groups[btn.toggleGroup];
26542            if(!g){
26543                g = groups[btn.toggleGroup] = [];
26544            }
26545            g.push(btn);
26546            btn.on("toggle", toggleGroup);
26547        },
26548        
26549        unregister : function(btn){
26550            if(!btn.toggleGroup){
26551                return;
26552            }
26553            var g = groups[btn.toggleGroup];
26554            if(g){
26555                g.remove(btn);
26556                btn.un("toggle", toggleGroup);
26557            }
26558        }
26559    };
26560 }();/*
26561  * Based on:
26562  * Ext JS Library 1.1.1
26563  * Copyright(c) 2006-2007, Ext JS, LLC.
26564  *
26565  * Originally Released Under LGPL - original licence link has changed is not relivant.
26566  *
26567  * Fork - LGPL
26568  * <script type="text/javascript">
26569  */
26570  
26571 /**
26572  * @class Roo.SplitButton
26573  * @extends Roo.Button
26574  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26575  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26576  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26577  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26578  * @cfg {String} arrowTooltip The title attribute of the arrow
26579  * @constructor
26580  * Create a new menu button
26581  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26582  * @param {Object} config The config object
26583  */
26584 Roo.SplitButton = function(renderTo, config){
26585     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26586     /**
26587      * @event arrowclick
26588      * Fires when this button's arrow is clicked
26589      * @param {SplitButton} this
26590      * @param {EventObject} e The click event
26591      */
26592     this.addEvents({"arrowclick":true});
26593 };
26594
26595 Roo.extend(Roo.SplitButton, Roo.Button, {
26596     render : function(renderTo){
26597         // this is one sweet looking template!
26598         var tpl = new Roo.Template(
26599             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26600             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26601             '<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>',
26602             "</tbody></table></td><td>",
26603             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26604             '<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>',
26605             "</tbody></table></td></tr></table>"
26606         );
26607         var btn = tpl.append(renderTo, [this.text, this.type], true);
26608         var btnEl = btn.child("button");
26609         if(this.cls){
26610             btn.addClass(this.cls);
26611         }
26612         if(this.icon){
26613             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26614         }
26615         if(this.iconCls){
26616             btnEl.addClass(this.iconCls);
26617             if(!this.cls){
26618                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26619             }
26620         }
26621         this.el = btn;
26622         if(this.handleMouseEvents){
26623             btn.on("mouseover", this.onMouseOver, this);
26624             btn.on("mouseout", this.onMouseOut, this);
26625             btn.on("mousedown", this.onMouseDown, this);
26626             btn.on("mouseup", this.onMouseUp, this);
26627         }
26628         btn.on(this.clickEvent, this.onClick, this);
26629         if(this.tooltip){
26630             if(typeof this.tooltip == 'object'){
26631                 Roo.QuickTips.tips(Roo.apply({
26632                       target: btnEl.id
26633                 }, this.tooltip));
26634             } else {
26635                 btnEl.dom[this.tooltipType] = this.tooltip;
26636             }
26637         }
26638         if(this.arrowTooltip){
26639             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26640         }
26641         if(this.hidden){
26642             this.hide();
26643         }
26644         if(this.disabled){
26645             this.disable();
26646         }
26647         if(this.pressed){
26648             this.el.addClass("x-btn-pressed");
26649         }
26650         if(Roo.isIE && !Roo.isIE7){
26651             this.autoWidth.defer(1, this);
26652         }else{
26653             this.autoWidth();
26654         }
26655         if(this.menu){
26656             this.menu.on("show", this.onMenuShow, this);
26657             this.menu.on("hide", this.onMenuHide, this);
26658         }
26659         this.fireEvent('render', this);
26660     },
26661
26662     // private
26663     autoWidth : function(){
26664         if(this.el){
26665             var tbl = this.el.child("table:first");
26666             var tbl2 = this.el.child("table:last");
26667             this.el.setWidth("auto");
26668             tbl.setWidth("auto");
26669             if(Roo.isIE7 && Roo.isStrict){
26670                 var ib = this.el.child('button:first');
26671                 if(ib && ib.getWidth() > 20){
26672                     ib.clip();
26673                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26674                 }
26675             }
26676             if(this.minWidth){
26677                 if(this.hidden){
26678                     this.el.beginMeasure();
26679                 }
26680                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26681                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26682                 }
26683                 if(this.hidden){
26684                     this.el.endMeasure();
26685                 }
26686             }
26687             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26688         } 
26689     },
26690     /**
26691      * Sets this button's click handler
26692      * @param {Function} handler The function to call when the button is clicked
26693      * @param {Object} scope (optional) Scope for the function passed above
26694      */
26695     setHandler : function(handler, scope){
26696         this.handler = handler;
26697         this.scope = scope;  
26698     },
26699     
26700     /**
26701      * Sets this button's arrow click handler
26702      * @param {Function} handler The function to call when the arrow is clicked
26703      * @param {Object} scope (optional) Scope for the function passed above
26704      */
26705     setArrowHandler : function(handler, scope){
26706         this.arrowHandler = handler;
26707         this.scope = scope;  
26708     },
26709     
26710     /**
26711      * Focus the button
26712      */
26713     focus : function(){
26714         if(this.el){
26715             this.el.child("button:first").focus();
26716         }
26717     },
26718
26719     // private
26720     onClick : function(e){
26721         e.preventDefault();
26722         if(!this.disabled){
26723             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26724                 if(this.menu && !this.menu.isVisible()){
26725                     this.menu.show(this.el, this.menuAlign);
26726                 }
26727                 this.fireEvent("arrowclick", this, e);
26728                 if(this.arrowHandler){
26729                     this.arrowHandler.call(this.scope || this, this, e);
26730                 }
26731             }else{
26732                 this.fireEvent("click", this, e);
26733                 if(this.handler){
26734                     this.handler.call(this.scope || this, this, e);
26735                 }
26736             }
26737         }
26738     },
26739     // private
26740     onMouseDown : function(e){
26741         if(!this.disabled){
26742             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26743         }
26744     },
26745     // private
26746     onMouseUp : function(e){
26747         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26748     }   
26749 });
26750
26751
26752 // backwards compat
26753 Roo.MenuButton = Roo.SplitButton;/*
26754  * Based on:
26755  * Ext JS Library 1.1.1
26756  * Copyright(c) 2006-2007, Ext JS, LLC.
26757  *
26758  * Originally Released Under LGPL - original licence link has changed is not relivant.
26759  *
26760  * Fork - LGPL
26761  * <script type="text/javascript">
26762  */
26763
26764 /**
26765  * @class Roo.Toolbar
26766  * Basic Toolbar class.
26767  * @constructor
26768  * Creates a new Toolbar
26769  * @param {Object} container The config object
26770  */ 
26771 Roo.Toolbar = function(container, buttons, config)
26772 {
26773     /// old consturctor format still supported..
26774     if(container instanceof Array){ // omit the container for later rendering
26775         buttons = container;
26776         config = buttons;
26777         container = null;
26778     }
26779     if (typeof(container) == 'object' && container.xtype) {
26780         config = container;
26781         container = config.container;
26782         buttons = config.buttons || []; // not really - use items!!
26783     }
26784     var xitems = [];
26785     if (config && config.items) {
26786         xitems = config.items;
26787         delete config.items;
26788     }
26789     Roo.apply(this, config);
26790     this.buttons = buttons;
26791     
26792     if(container){
26793         this.render(container);
26794     }
26795     this.xitems = xitems;
26796     Roo.each(xitems, function(b) {
26797         this.add(b);
26798     }, this);
26799     
26800 };
26801
26802 Roo.Toolbar.prototype = {
26803     /**
26804      * @cfg {Array} items
26805      * array of button configs or elements to add (will be converted to a MixedCollection)
26806      */
26807     
26808     /**
26809      * @cfg {String/HTMLElement/Element} container
26810      * The id or element that will contain the toolbar
26811      */
26812     // private
26813     render : function(ct){
26814         this.el = Roo.get(ct);
26815         if(this.cls){
26816             this.el.addClass(this.cls);
26817         }
26818         // using a table allows for vertical alignment
26819         // 100% width is needed by Safari...
26820         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26821         this.tr = this.el.child("tr", true);
26822         var autoId = 0;
26823         this.items = new Roo.util.MixedCollection(false, function(o){
26824             return o.id || ("item" + (++autoId));
26825         });
26826         if(this.buttons){
26827             this.add.apply(this, this.buttons);
26828             delete this.buttons;
26829         }
26830     },
26831
26832     /**
26833      * Adds element(s) to the toolbar -- this function takes a variable number of 
26834      * arguments of mixed type and adds them to the toolbar.
26835      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26836      * <ul>
26837      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26838      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26839      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26840      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26841      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26842      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26843      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26844      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26845      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26846      * </ul>
26847      * @param {Mixed} arg2
26848      * @param {Mixed} etc.
26849      */
26850     add : function(){
26851         var a = arguments, l = a.length;
26852         for(var i = 0; i < l; i++){
26853             this._add(a[i]);
26854         }
26855     },
26856     // private..
26857     _add : function(el) {
26858         
26859         if (el.xtype) {
26860             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26861         }
26862         
26863         if (el.applyTo){ // some kind of form field
26864             return this.addField(el);
26865         } 
26866         if (el.render){ // some kind of Toolbar.Item
26867             return this.addItem(el);
26868         }
26869         if (typeof el == "string"){ // string
26870             if(el == "separator" || el == "-"){
26871                 return this.addSeparator();
26872             }
26873             if (el == " "){
26874                 return this.addSpacer();
26875             }
26876             if(el == "->"){
26877                 return this.addFill();
26878             }
26879             return this.addText(el);
26880             
26881         }
26882         if(el.tagName){ // element
26883             return this.addElement(el);
26884         }
26885         if(typeof el == "object"){ // must be button config?
26886             return this.addButton(el);
26887         }
26888         // and now what?!?!
26889         return false;
26890         
26891     },
26892     
26893     /**
26894      * Add an Xtype element
26895      * @param {Object} xtype Xtype Object
26896      * @return {Object} created Object
26897      */
26898     addxtype : function(e){
26899         return this.add(e);  
26900     },
26901     
26902     /**
26903      * Returns the Element for this toolbar.
26904      * @return {Roo.Element}
26905      */
26906     getEl : function(){
26907         return this.el;  
26908     },
26909     
26910     /**
26911      * Adds a separator
26912      * @return {Roo.Toolbar.Item} The separator item
26913      */
26914     addSeparator : function(){
26915         return this.addItem(new Roo.Toolbar.Separator());
26916     },
26917
26918     /**
26919      * Adds a spacer element
26920      * @return {Roo.Toolbar.Spacer} The spacer item
26921      */
26922     addSpacer : function(){
26923         return this.addItem(new Roo.Toolbar.Spacer());
26924     },
26925
26926     /**
26927      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26928      * @return {Roo.Toolbar.Fill} The fill item
26929      */
26930     addFill : function(){
26931         return this.addItem(new Roo.Toolbar.Fill());
26932     },
26933
26934     /**
26935      * Adds any standard HTML element to the toolbar
26936      * @param {String/HTMLElement/Element} el The element or id of the element to add
26937      * @return {Roo.Toolbar.Item} The element's item
26938      */
26939     addElement : function(el){
26940         return this.addItem(new Roo.Toolbar.Item(el));
26941     },
26942     /**
26943      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26944      * @type Roo.util.MixedCollection  
26945      */
26946     items : false,
26947      
26948     /**
26949      * Adds any Toolbar.Item or subclass
26950      * @param {Roo.Toolbar.Item} item
26951      * @return {Roo.Toolbar.Item} The item
26952      */
26953     addItem : function(item){
26954         var td = this.nextBlock();
26955         item.render(td);
26956         this.items.add(item);
26957         return item;
26958     },
26959     
26960     /**
26961      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26962      * @param {Object/Array} config A button config or array of configs
26963      * @return {Roo.Toolbar.Button/Array}
26964      */
26965     addButton : function(config){
26966         if(config instanceof Array){
26967             var buttons = [];
26968             for(var i = 0, len = config.length; i < len; i++) {
26969                 buttons.push(this.addButton(config[i]));
26970             }
26971             return buttons;
26972         }
26973         var b = config;
26974         if(!(config instanceof Roo.Toolbar.Button)){
26975             b = config.split ?
26976                 new Roo.Toolbar.SplitButton(config) :
26977                 new Roo.Toolbar.Button(config);
26978         }
26979         var td = this.nextBlock();
26980         b.render(td);
26981         this.items.add(b);
26982         return b;
26983     },
26984     
26985     /**
26986      * Adds text to the toolbar
26987      * @param {String} text The text to add
26988      * @return {Roo.Toolbar.Item} The element's item
26989      */
26990     addText : function(text){
26991         return this.addItem(new Roo.Toolbar.TextItem(text));
26992     },
26993     
26994     /**
26995      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26996      * @param {Number} index The index where the item is to be inserted
26997      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26998      * @return {Roo.Toolbar.Button/Item}
26999      */
27000     insertButton : function(index, item){
27001         if(item instanceof Array){
27002             var buttons = [];
27003             for(var i = 0, len = item.length; i < len; i++) {
27004                buttons.push(this.insertButton(index + i, item[i]));
27005             }
27006             return buttons;
27007         }
27008         if (!(item instanceof Roo.Toolbar.Button)){
27009            item = new Roo.Toolbar.Button(item);
27010         }
27011         var td = document.createElement("td");
27012         this.tr.insertBefore(td, this.tr.childNodes[index]);
27013         item.render(td);
27014         this.items.insert(index, item);
27015         return item;
27016     },
27017     
27018     /**
27019      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27020      * @param {Object} config
27021      * @return {Roo.Toolbar.Item} The element's item
27022      */
27023     addDom : function(config, returnEl){
27024         var td = this.nextBlock();
27025         Roo.DomHelper.overwrite(td, config);
27026         var ti = new Roo.Toolbar.Item(td.firstChild);
27027         ti.render(td);
27028         this.items.add(ti);
27029         return ti;
27030     },
27031
27032     /**
27033      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27034      * @type Roo.util.MixedCollection  
27035      */
27036     fields : false,
27037     
27038     /**
27039      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27040      * Note: the field should not have been rendered yet. For a field that has already been
27041      * rendered, use {@link #addElement}.
27042      * @param {Roo.form.Field} field
27043      * @return {Roo.ToolbarItem}
27044      */
27045      
27046       
27047     addField : function(field) {
27048         if (!this.fields) {
27049             var autoId = 0;
27050             this.fields = new Roo.util.MixedCollection(false, function(o){
27051                 return o.id || ("item" + (++autoId));
27052             });
27053
27054         }
27055         
27056         var td = this.nextBlock();
27057         field.render(td);
27058         var ti = new Roo.Toolbar.Item(td.firstChild);
27059         ti.render(td);
27060         this.items.add(ti);
27061         this.fields.add(field);
27062         return ti;
27063     },
27064     /**
27065      * Hide the toolbar
27066      * @method hide
27067      */
27068      
27069       
27070     hide : function()
27071     {
27072         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27073         this.el.child('div').hide();
27074     },
27075     /**
27076      * Show the toolbar
27077      * @method show
27078      */
27079     show : function()
27080     {
27081         this.el.child('div').show();
27082     },
27083       
27084     // private
27085     nextBlock : function(){
27086         var td = document.createElement("td");
27087         this.tr.appendChild(td);
27088         return td;
27089     },
27090
27091     // private
27092     destroy : function(){
27093         if(this.items){ // rendered?
27094             Roo.destroy.apply(Roo, this.items.items);
27095         }
27096         if(this.fields){ // rendered?
27097             Roo.destroy.apply(Roo, this.fields.items);
27098         }
27099         Roo.Element.uncache(this.el, this.tr);
27100     }
27101 };
27102
27103 /**
27104  * @class Roo.Toolbar.Item
27105  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27106  * @constructor
27107  * Creates a new Item
27108  * @param {HTMLElement} el 
27109  */
27110 Roo.Toolbar.Item = function(el){
27111     this.el = Roo.getDom(el);
27112     this.id = Roo.id(this.el);
27113     this.hidden = false;
27114 };
27115
27116 Roo.Toolbar.Item.prototype = {
27117     
27118     /**
27119      * Get this item's HTML Element
27120      * @return {HTMLElement}
27121      */
27122     getEl : function(){
27123        return this.el;  
27124     },
27125
27126     // private
27127     render : function(td){
27128         this.td = td;
27129         td.appendChild(this.el);
27130     },
27131     
27132     /**
27133      * Removes and destroys this item.
27134      */
27135     destroy : function(){
27136         this.td.parentNode.removeChild(this.td);
27137     },
27138     
27139     /**
27140      * Shows this item.
27141      */
27142     show: function(){
27143         this.hidden = false;
27144         this.td.style.display = "";
27145     },
27146     
27147     /**
27148      * Hides this item.
27149      */
27150     hide: function(){
27151         this.hidden = true;
27152         this.td.style.display = "none";
27153     },
27154     
27155     /**
27156      * Convenience function for boolean show/hide.
27157      * @param {Boolean} visible true to show/false to hide
27158      */
27159     setVisible: function(visible){
27160         if(visible) {
27161             this.show();
27162         }else{
27163             this.hide();
27164         }
27165     },
27166     
27167     /**
27168      * Try to focus this item.
27169      */
27170     focus : function(){
27171         Roo.fly(this.el).focus();
27172     },
27173     
27174     /**
27175      * Disables this item.
27176      */
27177     disable : function(){
27178         Roo.fly(this.td).addClass("x-item-disabled");
27179         this.disabled = true;
27180         this.el.disabled = true;
27181     },
27182     
27183     /**
27184      * Enables this item.
27185      */
27186     enable : function(){
27187         Roo.fly(this.td).removeClass("x-item-disabled");
27188         this.disabled = false;
27189         this.el.disabled = false;
27190     }
27191 };
27192
27193
27194 /**
27195  * @class Roo.Toolbar.Separator
27196  * @extends Roo.Toolbar.Item
27197  * A simple toolbar separator class
27198  * @constructor
27199  * Creates a new Separator
27200  */
27201 Roo.Toolbar.Separator = function(){
27202     var s = document.createElement("span");
27203     s.className = "ytb-sep";
27204     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27205 };
27206 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27207     enable:Roo.emptyFn,
27208     disable:Roo.emptyFn,
27209     focus:Roo.emptyFn
27210 });
27211
27212 /**
27213  * @class Roo.Toolbar.Spacer
27214  * @extends Roo.Toolbar.Item
27215  * A simple element that adds extra horizontal space to a toolbar.
27216  * @constructor
27217  * Creates a new Spacer
27218  */
27219 Roo.Toolbar.Spacer = function(){
27220     var s = document.createElement("div");
27221     s.className = "ytb-spacer";
27222     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27223 };
27224 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27225     enable:Roo.emptyFn,
27226     disable:Roo.emptyFn,
27227     focus:Roo.emptyFn
27228 });
27229
27230 /**
27231  * @class Roo.Toolbar.Fill
27232  * @extends Roo.Toolbar.Spacer
27233  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27234  * @constructor
27235  * Creates a new Spacer
27236  */
27237 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27238     // private
27239     render : function(td){
27240         td.style.width = '100%';
27241         Roo.Toolbar.Fill.superclass.render.call(this, td);
27242     }
27243 });
27244
27245 /**
27246  * @class Roo.Toolbar.TextItem
27247  * @extends Roo.Toolbar.Item
27248  * A simple class that renders text directly into a toolbar.
27249  * @constructor
27250  * Creates a new TextItem
27251  * @param {String} text
27252  */
27253 Roo.Toolbar.TextItem = function(text){
27254     if (typeof(text) == 'object') {
27255         text = text.text;
27256     }
27257     var s = document.createElement("span");
27258     s.className = "ytb-text";
27259     s.innerHTML = text;
27260     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27261 };
27262 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27263     enable:Roo.emptyFn,
27264     disable:Roo.emptyFn,
27265     focus:Roo.emptyFn
27266 });
27267
27268 /**
27269  * @class Roo.Toolbar.Button
27270  * @extends Roo.Button
27271  * A button that renders into a toolbar.
27272  * @constructor
27273  * Creates a new Button
27274  * @param {Object} config A standard {@link Roo.Button} config object
27275  */
27276 Roo.Toolbar.Button = function(config){
27277     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27278 };
27279 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27280     render : function(td){
27281         this.td = td;
27282         Roo.Toolbar.Button.superclass.render.call(this, td);
27283     },
27284     
27285     /**
27286      * Removes and destroys this button
27287      */
27288     destroy : function(){
27289         Roo.Toolbar.Button.superclass.destroy.call(this);
27290         this.td.parentNode.removeChild(this.td);
27291     },
27292     
27293     /**
27294      * Shows this button
27295      */
27296     show: function(){
27297         this.hidden = false;
27298         this.td.style.display = "";
27299     },
27300     
27301     /**
27302      * Hides this button
27303      */
27304     hide: function(){
27305         this.hidden = true;
27306         this.td.style.display = "none";
27307     },
27308
27309     /**
27310      * Disables this item
27311      */
27312     disable : function(){
27313         Roo.fly(this.td).addClass("x-item-disabled");
27314         this.disabled = true;
27315     },
27316
27317     /**
27318      * Enables this item
27319      */
27320     enable : function(){
27321         Roo.fly(this.td).removeClass("x-item-disabled");
27322         this.disabled = false;
27323     }
27324 });
27325 // backwards compat
27326 Roo.ToolbarButton = Roo.Toolbar.Button;
27327
27328 /**
27329  * @class Roo.Toolbar.SplitButton
27330  * @extends Roo.SplitButton
27331  * A menu button that renders into a toolbar.
27332  * @constructor
27333  * Creates a new SplitButton
27334  * @param {Object} config A standard {@link Roo.SplitButton} config object
27335  */
27336 Roo.Toolbar.SplitButton = function(config){
27337     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27338 };
27339 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27340     render : function(td){
27341         this.td = td;
27342         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27343     },
27344     
27345     /**
27346      * Removes and destroys this button
27347      */
27348     destroy : function(){
27349         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27350         this.td.parentNode.removeChild(this.td);
27351     },
27352     
27353     /**
27354      * Shows this button
27355      */
27356     show: function(){
27357         this.hidden = false;
27358         this.td.style.display = "";
27359     },
27360     
27361     /**
27362      * Hides this button
27363      */
27364     hide: function(){
27365         this.hidden = true;
27366         this.td.style.display = "none";
27367     }
27368 });
27369
27370 // backwards compat
27371 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27372  * Based on:
27373  * Ext JS Library 1.1.1
27374  * Copyright(c) 2006-2007, Ext JS, LLC.
27375  *
27376  * Originally Released Under LGPL - original licence link has changed is not relivant.
27377  *
27378  * Fork - LGPL
27379  * <script type="text/javascript">
27380  */
27381  
27382 /**
27383  * @class Roo.PagingToolbar
27384  * @extends Roo.Toolbar
27385  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27386  * @constructor
27387  * Create a new PagingToolbar
27388  * @param {Object} config The config object
27389  */
27390 Roo.PagingToolbar = function(el, ds, config)
27391 {
27392     // old args format still supported... - xtype is prefered..
27393     if (typeof(el) == 'object' && el.xtype) {
27394         // created from xtype...
27395         config = el;
27396         ds = el.dataSource;
27397         el = config.container;
27398     }
27399     var items = [];
27400     if (config.items) {
27401         items = config.items;
27402         config.items = [];
27403     }
27404     
27405     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27406     this.ds = ds;
27407     this.cursor = 0;
27408     this.renderButtons(this.el);
27409     this.bind(ds);
27410     
27411     // supprot items array.
27412    
27413     Roo.each(items, function(e) {
27414         this.add(Roo.factory(e));
27415     },this);
27416     
27417 };
27418
27419 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27420     /**
27421      * @cfg {Roo.data.Store} dataSource
27422      * The underlying data store providing the paged data
27423      */
27424     /**
27425      * @cfg {String/HTMLElement/Element} container
27426      * container The id or element that will contain the toolbar
27427      */
27428     /**
27429      * @cfg {Boolean} displayInfo
27430      * True to display the displayMsg (defaults to false)
27431      */
27432     /**
27433      * @cfg {Number} pageSize
27434      * The number of records to display per page (defaults to 20)
27435      */
27436     pageSize: 20,
27437     /**
27438      * @cfg {String} displayMsg
27439      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27440      */
27441     displayMsg : 'Displaying {0} - {1} of {2}',
27442     /**
27443      * @cfg {String} emptyMsg
27444      * The message to display when no records are found (defaults to "No data to display")
27445      */
27446     emptyMsg : 'No data to display',
27447     /**
27448      * Customizable piece of the default paging text (defaults to "Page")
27449      * @type String
27450      */
27451     beforePageText : "Page",
27452     /**
27453      * Customizable piece of the default paging text (defaults to "of %0")
27454      * @type String
27455      */
27456     afterPageText : "of {0}",
27457     /**
27458      * Customizable piece of the default paging text (defaults to "First Page")
27459      * @type String
27460      */
27461     firstText : "First Page",
27462     /**
27463      * Customizable piece of the default paging text (defaults to "Previous Page")
27464      * @type String
27465      */
27466     prevText : "Previous Page",
27467     /**
27468      * Customizable piece of the default paging text (defaults to "Next Page")
27469      * @type String
27470      */
27471     nextText : "Next Page",
27472     /**
27473      * Customizable piece of the default paging text (defaults to "Last Page")
27474      * @type String
27475      */
27476     lastText : "Last Page",
27477     /**
27478      * Customizable piece of the default paging text (defaults to "Refresh")
27479      * @type String
27480      */
27481     refreshText : "Refresh",
27482
27483     // private
27484     renderButtons : function(el){
27485         Roo.PagingToolbar.superclass.render.call(this, el);
27486         this.first = this.addButton({
27487             tooltip: this.firstText,
27488             cls: "x-btn-icon x-grid-page-first",
27489             disabled: true,
27490             handler: this.onClick.createDelegate(this, ["first"])
27491         });
27492         this.prev = this.addButton({
27493             tooltip: this.prevText,
27494             cls: "x-btn-icon x-grid-page-prev",
27495             disabled: true,
27496             handler: this.onClick.createDelegate(this, ["prev"])
27497         });
27498         //this.addSeparator();
27499         this.add(this.beforePageText);
27500         this.field = Roo.get(this.addDom({
27501            tag: "input",
27502            type: "text",
27503            size: "3",
27504            value: "1",
27505            cls: "x-grid-page-number"
27506         }).el);
27507         this.field.on("keydown", this.onPagingKeydown, this);
27508         this.field.on("focus", function(){this.dom.select();});
27509         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27510         this.field.setHeight(18);
27511         //this.addSeparator();
27512         this.next = this.addButton({
27513             tooltip: this.nextText,
27514             cls: "x-btn-icon x-grid-page-next",
27515             disabled: true,
27516             handler: this.onClick.createDelegate(this, ["next"])
27517         });
27518         this.last = this.addButton({
27519             tooltip: this.lastText,
27520             cls: "x-btn-icon x-grid-page-last",
27521             disabled: true,
27522             handler: this.onClick.createDelegate(this, ["last"])
27523         });
27524         //this.addSeparator();
27525         this.loading = this.addButton({
27526             tooltip: this.refreshText,
27527             cls: "x-btn-icon x-grid-loading",
27528             handler: this.onClick.createDelegate(this, ["refresh"])
27529         });
27530
27531         if(this.displayInfo){
27532             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27533         }
27534     },
27535
27536     // private
27537     updateInfo : function(){
27538         if(this.displayEl){
27539             var count = this.ds.getCount();
27540             var msg = count == 0 ?
27541                 this.emptyMsg :
27542                 String.format(
27543                     this.displayMsg,
27544                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27545                 );
27546             this.displayEl.update(msg);
27547         }
27548     },
27549
27550     // private
27551     onLoad : function(ds, r, o){
27552        this.cursor = o.params ? o.params.start : 0;
27553        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27554
27555        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27556        this.field.dom.value = ap;
27557        this.first.setDisabled(ap == 1);
27558        this.prev.setDisabled(ap == 1);
27559        this.next.setDisabled(ap == ps);
27560        this.last.setDisabled(ap == ps);
27561        this.loading.enable();
27562        this.updateInfo();
27563     },
27564
27565     // private
27566     getPageData : function(){
27567         var total = this.ds.getTotalCount();
27568         return {
27569             total : total,
27570             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27571             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27572         };
27573     },
27574
27575     // private
27576     onLoadError : function(){
27577         this.loading.enable();
27578     },
27579
27580     // private
27581     onPagingKeydown : function(e){
27582         var k = e.getKey();
27583         var d = this.getPageData();
27584         if(k == e.RETURN){
27585             var v = this.field.dom.value, pageNum;
27586             if(!v || isNaN(pageNum = parseInt(v, 10))){
27587                 this.field.dom.value = d.activePage;
27588                 return;
27589             }
27590             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27591             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27592             e.stopEvent();
27593         }
27594         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))
27595         {
27596           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27597           this.field.dom.value = pageNum;
27598           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27599           e.stopEvent();
27600         }
27601         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27602         {
27603           var v = this.field.dom.value, pageNum; 
27604           var increment = (e.shiftKey) ? 10 : 1;
27605           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27606             increment *= -1;
27607           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27608             this.field.dom.value = d.activePage;
27609             return;
27610           }
27611           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27612           {
27613             this.field.dom.value = parseInt(v, 10) + increment;
27614             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27615             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27616           }
27617           e.stopEvent();
27618         }
27619     },
27620
27621     // private
27622     beforeLoad : function(){
27623         if(this.loading){
27624             this.loading.disable();
27625         }
27626     },
27627
27628     // private
27629     onClick : function(which){
27630         var ds = this.ds;
27631         switch(which){
27632             case "first":
27633                 ds.load({params:{start: 0, limit: this.pageSize}});
27634             break;
27635             case "prev":
27636                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27637             break;
27638             case "next":
27639                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27640             break;
27641             case "last":
27642                 var total = ds.getTotalCount();
27643                 var extra = total % this.pageSize;
27644                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27645                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27646             break;
27647             case "refresh":
27648                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27649             break;
27650         }
27651     },
27652
27653     /**
27654      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27655      * @param {Roo.data.Store} store The data store to unbind
27656      */
27657     unbind : function(ds){
27658         ds.un("beforeload", this.beforeLoad, this);
27659         ds.un("load", this.onLoad, this);
27660         ds.un("loadexception", this.onLoadError, this);
27661         ds.un("remove", this.updateInfo, this);
27662         ds.un("add", this.updateInfo, this);
27663         this.ds = undefined;
27664     },
27665
27666     /**
27667      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27668      * @param {Roo.data.Store} store The data store to bind
27669      */
27670     bind : function(ds){
27671         ds.on("beforeload", this.beforeLoad, this);
27672         ds.on("load", this.onLoad, this);
27673         ds.on("loadexception", this.onLoadError, this);
27674         ds.on("remove", this.updateInfo, this);
27675         ds.on("add", this.updateInfo, this);
27676         this.ds = ds;
27677     }
27678 });/*
27679  * Based on:
27680  * Ext JS Library 1.1.1
27681  * Copyright(c) 2006-2007, Ext JS, LLC.
27682  *
27683  * Originally Released Under LGPL - original licence link has changed is not relivant.
27684  *
27685  * Fork - LGPL
27686  * <script type="text/javascript">
27687  */
27688
27689 /**
27690  * @class Roo.Resizable
27691  * @extends Roo.util.Observable
27692  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27693  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27694  * 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
27695  * the element will be wrapped for you automatically.</p>
27696  * <p>Here is the list of valid resize handles:</p>
27697  * <pre>
27698 Value   Description
27699 ------  -------------------
27700  'n'     north
27701  's'     south
27702  'e'     east
27703  'w'     west
27704  'nw'    northwest
27705  'sw'    southwest
27706  'se'    southeast
27707  'ne'    northeast
27708  'hd'    horizontal drag
27709  'all'   all
27710 </pre>
27711  * <p>Here's an example showing the creation of a typical Resizable:</p>
27712  * <pre><code>
27713 var resizer = new Roo.Resizable("element-id", {
27714     handles: 'all',
27715     minWidth: 200,
27716     minHeight: 100,
27717     maxWidth: 500,
27718     maxHeight: 400,
27719     pinned: true
27720 });
27721 resizer.on("resize", myHandler);
27722 </code></pre>
27723  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27724  * resizer.east.setDisplayed(false);</p>
27725  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27726  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27727  * resize operation's new size (defaults to [0, 0])
27728  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27729  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27730  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27731  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27732  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27733  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27734  * @cfg {Number} width The width of the element in pixels (defaults to null)
27735  * @cfg {Number} height The height of the element in pixels (defaults to null)
27736  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27737  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27738  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27739  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27740  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27741  * in favor of the handles config option (defaults to false)
27742  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27743  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27744  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27745  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27746  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27747  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27748  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27749  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27750  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27751  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27752  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27753  * @constructor
27754  * Create a new resizable component
27755  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27756  * @param {Object} config configuration options
27757   */
27758 Roo.Resizable = function(el, config)
27759 {
27760     this.el = Roo.get(el);
27761
27762     if(config && config.wrap){
27763         config.resizeChild = this.el;
27764         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27765         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27766         this.el.setStyle("overflow", "hidden");
27767         this.el.setPositioning(config.resizeChild.getPositioning());
27768         config.resizeChild.clearPositioning();
27769         if(!config.width || !config.height){
27770             var csize = config.resizeChild.getSize();
27771             this.el.setSize(csize.width, csize.height);
27772         }
27773         if(config.pinned && !config.adjustments){
27774             config.adjustments = "auto";
27775         }
27776     }
27777
27778     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27779     this.proxy.unselectable();
27780     this.proxy.enableDisplayMode('block');
27781
27782     Roo.apply(this, config);
27783
27784     if(this.pinned){
27785         this.disableTrackOver = true;
27786         this.el.addClass("x-resizable-pinned");
27787     }
27788     // if the element isn't positioned, make it relative
27789     var position = this.el.getStyle("position");
27790     if(position != "absolute" && position != "fixed"){
27791         this.el.setStyle("position", "relative");
27792     }
27793     if(!this.handles){ // no handles passed, must be legacy style
27794         this.handles = 's,e,se';
27795         if(this.multiDirectional){
27796             this.handles += ',n,w';
27797         }
27798     }
27799     if(this.handles == "all"){
27800         this.handles = "n s e w ne nw se sw";
27801     }
27802     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27803     var ps = Roo.Resizable.positions;
27804     for(var i = 0, len = hs.length; i < len; i++){
27805         if(hs[i] && ps[hs[i]]){
27806             var pos = ps[hs[i]];
27807             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27808         }
27809     }
27810     // legacy
27811     this.corner = this.southeast;
27812     
27813     // updateBox = the box can move..
27814     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27815         this.updateBox = true;
27816     }
27817
27818     this.activeHandle = null;
27819
27820     if(this.resizeChild){
27821         if(typeof this.resizeChild == "boolean"){
27822             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27823         }else{
27824             this.resizeChild = Roo.get(this.resizeChild, true);
27825         }
27826     }
27827     
27828     if(this.adjustments == "auto"){
27829         var rc = this.resizeChild;
27830         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27831         if(rc && (hw || hn)){
27832             rc.position("relative");
27833             rc.setLeft(hw ? hw.el.getWidth() : 0);
27834             rc.setTop(hn ? hn.el.getHeight() : 0);
27835         }
27836         this.adjustments = [
27837             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27838             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27839         ];
27840     }
27841
27842     if(this.draggable){
27843         this.dd = this.dynamic ?
27844             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27845         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27846     }
27847
27848     // public events
27849     this.addEvents({
27850         /**
27851          * @event beforeresize
27852          * Fired before resize is allowed. Set enabled to false to cancel resize.
27853          * @param {Roo.Resizable} this
27854          * @param {Roo.EventObject} e The mousedown event
27855          */
27856         "beforeresize" : true,
27857         /**
27858          * @event resize
27859          * Fired after a resize.
27860          * @param {Roo.Resizable} this
27861          * @param {Number} width The new width
27862          * @param {Number} height The new height
27863          * @param {Roo.EventObject} e The mouseup event
27864          */
27865         "resize" : true
27866     });
27867
27868     if(this.width !== null && this.height !== null){
27869         this.resizeTo(this.width, this.height);
27870     }else{
27871         this.updateChildSize();
27872     }
27873     if(Roo.isIE){
27874         this.el.dom.style.zoom = 1;
27875     }
27876     Roo.Resizable.superclass.constructor.call(this);
27877 };
27878
27879 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27880         resizeChild : false,
27881         adjustments : [0, 0],
27882         minWidth : 5,
27883         minHeight : 5,
27884         maxWidth : 10000,
27885         maxHeight : 10000,
27886         enabled : true,
27887         animate : false,
27888         duration : .35,
27889         dynamic : false,
27890         handles : false,
27891         multiDirectional : false,
27892         disableTrackOver : false,
27893         easing : 'easeOutStrong',
27894         widthIncrement : 0,
27895         heightIncrement : 0,
27896         pinned : false,
27897         width : null,
27898         height : null,
27899         preserveRatio : false,
27900         transparent: false,
27901         minX: 0,
27902         minY: 0,
27903         draggable: false,
27904
27905         /**
27906          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27907          */
27908         constrainTo: undefined,
27909         /**
27910          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27911          */
27912         resizeRegion: undefined,
27913
27914
27915     /**
27916      * Perform a manual resize
27917      * @param {Number} width
27918      * @param {Number} height
27919      */
27920     resizeTo : function(width, height){
27921         this.el.setSize(width, height);
27922         this.updateChildSize();
27923         this.fireEvent("resize", this, width, height, null);
27924     },
27925
27926     // private
27927     startSizing : function(e, handle){
27928         this.fireEvent("beforeresize", this, e);
27929         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27930
27931             if(!this.overlay){
27932                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27933                 this.overlay.unselectable();
27934                 this.overlay.enableDisplayMode("block");
27935                 this.overlay.on("mousemove", this.onMouseMove, this);
27936                 this.overlay.on("mouseup", this.onMouseUp, this);
27937             }
27938             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27939
27940             this.resizing = true;
27941             this.startBox = this.el.getBox();
27942             this.startPoint = e.getXY();
27943             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27944                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27945
27946             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27947             this.overlay.show();
27948
27949             if(this.constrainTo) {
27950                 var ct = Roo.get(this.constrainTo);
27951                 this.resizeRegion = ct.getRegion().adjust(
27952                     ct.getFrameWidth('t'),
27953                     ct.getFrameWidth('l'),
27954                     -ct.getFrameWidth('b'),
27955                     -ct.getFrameWidth('r')
27956                 );
27957             }
27958
27959             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27960             this.proxy.show();
27961             this.proxy.setBox(this.startBox);
27962             if(!this.dynamic){
27963                 this.proxy.setStyle('visibility', 'visible');
27964             }
27965         }
27966     },
27967
27968     // private
27969     onMouseDown : function(handle, e){
27970         if(this.enabled){
27971             e.stopEvent();
27972             this.activeHandle = handle;
27973             this.startSizing(e, handle);
27974         }
27975     },
27976
27977     // private
27978     onMouseUp : function(e){
27979         var size = this.resizeElement();
27980         this.resizing = false;
27981         this.handleOut();
27982         this.overlay.hide();
27983         this.proxy.hide();
27984         this.fireEvent("resize", this, size.width, size.height, e);
27985     },
27986
27987     // private
27988     updateChildSize : function(){
27989         if(this.resizeChild){
27990             var el = this.el;
27991             var child = this.resizeChild;
27992             var adj = this.adjustments;
27993             if(el.dom.offsetWidth){
27994                 var b = el.getSize(true);
27995                 child.setSize(b.width+adj[0], b.height+adj[1]);
27996             }
27997             // Second call here for IE
27998             // The first call enables instant resizing and
27999             // the second call corrects scroll bars if they
28000             // exist
28001             if(Roo.isIE){
28002                 setTimeout(function(){
28003                     if(el.dom.offsetWidth){
28004                         var b = el.getSize(true);
28005                         child.setSize(b.width+adj[0], b.height+adj[1]);
28006                     }
28007                 }, 10);
28008             }
28009         }
28010     },
28011
28012     // private
28013     snap : function(value, inc, min){
28014         if(!inc || !value) return value;
28015         var newValue = value;
28016         var m = value % inc;
28017         if(m > 0){
28018             if(m > (inc/2)){
28019                 newValue = value + (inc-m);
28020             }else{
28021                 newValue = value - m;
28022             }
28023         }
28024         return Math.max(min, newValue);
28025     },
28026
28027     // private
28028     resizeElement : function(){
28029         var box = this.proxy.getBox();
28030         if(this.updateBox){
28031             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28032         }else{
28033             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28034         }
28035         this.updateChildSize();
28036         if(!this.dynamic){
28037             this.proxy.hide();
28038         }
28039         return box;
28040     },
28041
28042     // private
28043     constrain : function(v, diff, m, mx){
28044         if(v - diff < m){
28045             diff = v - m;
28046         }else if(v - diff > mx){
28047             diff = mx - v;
28048         }
28049         return diff;
28050     },
28051
28052     // private
28053     onMouseMove : function(e){
28054         if(this.enabled){
28055             try{// try catch so if something goes wrong the user doesn't get hung
28056
28057             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28058                 return;
28059             }
28060
28061             //var curXY = this.startPoint;
28062             var curSize = this.curSize || this.startBox;
28063             var x = this.startBox.x, y = this.startBox.y;
28064             var ox = x, oy = y;
28065             var w = curSize.width, h = curSize.height;
28066             var ow = w, oh = h;
28067             var mw = this.minWidth, mh = this.minHeight;
28068             var mxw = this.maxWidth, mxh = this.maxHeight;
28069             var wi = this.widthIncrement;
28070             var hi = this.heightIncrement;
28071
28072             var eventXY = e.getXY();
28073             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28074             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28075
28076             var pos = this.activeHandle.position;
28077
28078             switch(pos){
28079                 case "east":
28080                     w += diffX;
28081                     w = Math.min(Math.max(mw, w), mxw);
28082                     break;
28083              
28084                 case "south":
28085                     h += diffY;
28086                     h = Math.min(Math.max(mh, h), mxh);
28087                     break;
28088                 case "southeast":
28089                     w += diffX;
28090                     h += diffY;
28091                     w = Math.min(Math.max(mw, w), mxw);
28092                     h = Math.min(Math.max(mh, h), mxh);
28093                     break;
28094                 case "north":
28095                     diffY = this.constrain(h, diffY, mh, mxh);
28096                     y += diffY;
28097                     h -= diffY;
28098                     break;
28099                 case "hdrag":
28100                     
28101                     if (wi) {
28102                         var adiffX = Math.abs(diffX);
28103                         var sub = (adiffX % wi); // how much 
28104                         if (sub > (wi/2)) { // far enough to snap
28105                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28106                         } else {
28107                             // remove difference.. 
28108                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28109                         }
28110                     }
28111                     x += diffX;
28112                     x = Math.max(this.minX, x);
28113                     break;
28114                 case "west":
28115                     diffX = this.constrain(w, diffX, mw, mxw);
28116                     x += diffX;
28117                     w -= diffX;
28118                     break;
28119                 case "northeast":
28120                     w += diffX;
28121                     w = Math.min(Math.max(mw, w), mxw);
28122                     diffY = this.constrain(h, diffY, mh, mxh);
28123                     y += diffY;
28124                     h -= diffY;
28125                     break;
28126                 case "northwest":
28127                     diffX = this.constrain(w, diffX, mw, mxw);
28128                     diffY = this.constrain(h, diffY, mh, mxh);
28129                     y += diffY;
28130                     h -= diffY;
28131                     x += diffX;
28132                     w -= diffX;
28133                     break;
28134                case "southwest":
28135                     diffX = this.constrain(w, diffX, mw, mxw);
28136                     h += diffY;
28137                     h = Math.min(Math.max(mh, h), mxh);
28138                     x += diffX;
28139                     w -= diffX;
28140                     break;
28141             }
28142
28143             var sw = this.snap(w, wi, mw);
28144             var sh = this.snap(h, hi, mh);
28145             if(sw != w || sh != h){
28146                 switch(pos){
28147                     case "northeast":
28148                         y -= sh - h;
28149                     break;
28150                     case "north":
28151                         y -= sh - h;
28152                         break;
28153                     case "southwest":
28154                         x -= sw - w;
28155                     break;
28156                     case "west":
28157                         x -= sw - w;
28158                         break;
28159                     case "northwest":
28160                         x -= sw - w;
28161                         y -= sh - h;
28162                     break;
28163                 }
28164                 w = sw;
28165                 h = sh;
28166             }
28167
28168             if(this.preserveRatio){
28169                 switch(pos){
28170                     case "southeast":
28171                     case "east":
28172                         h = oh * (w/ow);
28173                         h = Math.min(Math.max(mh, h), mxh);
28174                         w = ow * (h/oh);
28175                        break;
28176                     case "south":
28177                         w = ow * (h/oh);
28178                         w = Math.min(Math.max(mw, w), mxw);
28179                         h = oh * (w/ow);
28180                         break;
28181                     case "northeast":
28182                         w = ow * (h/oh);
28183                         w = Math.min(Math.max(mw, w), mxw);
28184                         h = oh * (w/ow);
28185                     break;
28186                     case "north":
28187                         var tw = w;
28188                         w = ow * (h/oh);
28189                         w = Math.min(Math.max(mw, w), mxw);
28190                         h = oh * (w/ow);
28191                         x += (tw - w) / 2;
28192                         break;
28193                     case "southwest":
28194                         h = oh * (w/ow);
28195                         h = Math.min(Math.max(mh, h), mxh);
28196                         var tw = w;
28197                         w = ow * (h/oh);
28198                         x += tw - w;
28199                         break;
28200                     case "west":
28201                         var th = h;
28202                         h = oh * (w/ow);
28203                         h = Math.min(Math.max(mh, h), mxh);
28204                         y += (th - h) / 2;
28205                         var tw = w;
28206                         w = ow * (h/oh);
28207                         x += tw - w;
28208                        break;
28209                     case "northwest":
28210                         var tw = w;
28211                         var th = h;
28212                         h = oh * (w/ow);
28213                         h = Math.min(Math.max(mh, h), mxh);
28214                         w = ow * (h/oh);
28215                         y += th - h;
28216                         x += tw - w;
28217                        break;
28218
28219                 }
28220             }
28221             if (pos == 'hdrag') {
28222                 w = ow;
28223             }
28224             this.proxy.setBounds(x, y, w, h);
28225             if(this.dynamic){
28226                 this.resizeElement();
28227             }
28228             }catch(e){}
28229         }
28230     },
28231
28232     // private
28233     handleOver : function(){
28234         if(this.enabled){
28235             this.el.addClass("x-resizable-over");
28236         }
28237     },
28238
28239     // private
28240     handleOut : function(){
28241         if(!this.resizing){
28242             this.el.removeClass("x-resizable-over");
28243         }
28244     },
28245
28246     /**
28247      * Returns the element this component is bound to.
28248      * @return {Roo.Element}
28249      */
28250     getEl : function(){
28251         return this.el;
28252     },
28253
28254     /**
28255      * Returns the resizeChild element (or null).
28256      * @return {Roo.Element}
28257      */
28258     getResizeChild : function(){
28259         return this.resizeChild;
28260     },
28261
28262     /**
28263      * Destroys this resizable. If the element was wrapped and
28264      * removeEl is not true then the element remains.
28265      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28266      */
28267     destroy : function(removeEl){
28268         this.proxy.remove();
28269         if(this.overlay){
28270             this.overlay.removeAllListeners();
28271             this.overlay.remove();
28272         }
28273         var ps = Roo.Resizable.positions;
28274         for(var k in ps){
28275             if(typeof ps[k] != "function" && this[ps[k]]){
28276                 var h = this[ps[k]];
28277                 h.el.removeAllListeners();
28278                 h.el.remove();
28279             }
28280         }
28281         if(removeEl){
28282             this.el.update("");
28283             this.el.remove();
28284         }
28285     }
28286 });
28287
28288 // private
28289 // hash to map config positions to true positions
28290 Roo.Resizable.positions = {
28291     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28292     hd: "hdrag"
28293 };
28294
28295 // private
28296 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28297     if(!this.tpl){
28298         // only initialize the template if resizable is used
28299         var tpl = Roo.DomHelper.createTemplate(
28300             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28301         );
28302         tpl.compile();
28303         Roo.Resizable.Handle.prototype.tpl = tpl;
28304     }
28305     this.position = pos;
28306     this.rz = rz;
28307     // show north drag fro topdra
28308     var handlepos = pos == 'hdrag' ? 'north' : pos;
28309     
28310     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28311     if (pos == 'hdrag') {
28312         this.el.setStyle('cursor', 'pointer');
28313     }
28314     this.el.unselectable();
28315     if(transparent){
28316         this.el.setOpacity(0);
28317     }
28318     this.el.on("mousedown", this.onMouseDown, this);
28319     if(!disableTrackOver){
28320         this.el.on("mouseover", this.onMouseOver, this);
28321         this.el.on("mouseout", this.onMouseOut, this);
28322     }
28323 };
28324
28325 // private
28326 Roo.Resizable.Handle.prototype = {
28327     afterResize : function(rz){
28328         // do nothing
28329     },
28330     // private
28331     onMouseDown : function(e){
28332         this.rz.onMouseDown(this, e);
28333     },
28334     // private
28335     onMouseOver : function(e){
28336         this.rz.handleOver(this, e);
28337     },
28338     // private
28339     onMouseOut : function(e){
28340         this.rz.handleOut(this, e);
28341     }
28342 };/*
28343  * Based on:
28344  * Ext JS Library 1.1.1
28345  * Copyright(c) 2006-2007, Ext JS, LLC.
28346  *
28347  * Originally Released Under LGPL - original licence link has changed is not relivant.
28348  *
28349  * Fork - LGPL
28350  * <script type="text/javascript">
28351  */
28352
28353 /**
28354  * @class Roo.Editor
28355  * @extends Roo.Component
28356  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28357  * @constructor
28358  * Create a new Editor
28359  * @param {Roo.form.Field} field The Field object (or descendant)
28360  * @param {Object} config The config object
28361  */
28362 Roo.Editor = function(field, config){
28363     Roo.Editor.superclass.constructor.call(this, config);
28364     this.field = field;
28365     this.addEvents({
28366         /**
28367              * @event beforestartedit
28368              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28369              * false from the handler of this event.
28370              * @param {Editor} this
28371              * @param {Roo.Element} boundEl The underlying element bound to this editor
28372              * @param {Mixed} value The field value being set
28373              */
28374         "beforestartedit" : true,
28375         /**
28376              * @event startedit
28377              * Fires when this editor is displayed
28378              * @param {Roo.Element} boundEl The underlying element bound to this editor
28379              * @param {Mixed} value The starting field value
28380              */
28381         "startedit" : true,
28382         /**
28383              * @event beforecomplete
28384              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28385              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28386              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28387              * event will not fire since no edit actually occurred.
28388              * @param {Editor} this
28389              * @param {Mixed} value The current field value
28390              * @param {Mixed} startValue The original field value
28391              */
28392         "beforecomplete" : true,
28393         /**
28394              * @event complete
28395              * Fires after editing is complete and any changed value has been written to the underlying field.
28396              * @param {Editor} this
28397              * @param {Mixed} value The current field value
28398              * @param {Mixed} startValue The original field value
28399              */
28400         "complete" : true,
28401         /**
28402          * @event specialkey
28403          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28404          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28405          * @param {Roo.form.Field} this
28406          * @param {Roo.EventObject} e The event object
28407          */
28408         "specialkey" : true
28409     });
28410 };
28411
28412 Roo.extend(Roo.Editor, Roo.Component, {
28413     /**
28414      * @cfg {Boolean/String} autosize
28415      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28416      * or "height" to adopt the height only (defaults to false)
28417      */
28418     /**
28419      * @cfg {Boolean} revertInvalid
28420      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28421      * validation fails (defaults to true)
28422      */
28423     /**
28424      * @cfg {Boolean} ignoreNoChange
28425      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28426      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28427      * will never be ignored.
28428      */
28429     /**
28430      * @cfg {Boolean} hideEl
28431      * False to keep the bound element visible while the editor is displayed (defaults to true)
28432      */
28433     /**
28434      * @cfg {Mixed} value
28435      * The data value of the underlying field (defaults to "")
28436      */
28437     value : "",
28438     /**
28439      * @cfg {String} alignment
28440      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28441      */
28442     alignment: "c-c?",
28443     /**
28444      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28445      * for bottom-right shadow (defaults to "frame")
28446      */
28447     shadow : "frame",
28448     /**
28449      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28450      */
28451     constrain : false,
28452     /**
28453      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28454      */
28455     completeOnEnter : false,
28456     /**
28457      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28458      */
28459     cancelOnEsc : false,
28460     /**
28461      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28462      */
28463     updateEl : false,
28464
28465     // private
28466     onRender : function(ct, position){
28467         this.el = new Roo.Layer({
28468             shadow: this.shadow,
28469             cls: "x-editor",
28470             parentEl : ct,
28471             shim : this.shim,
28472             shadowOffset:4,
28473             id: this.id,
28474             constrain: this.constrain
28475         });
28476         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28477         if(this.field.msgTarget != 'title'){
28478             this.field.msgTarget = 'qtip';
28479         }
28480         this.field.render(this.el);
28481         if(Roo.isGecko){
28482             this.field.el.dom.setAttribute('autocomplete', 'off');
28483         }
28484         this.field.on("specialkey", this.onSpecialKey, this);
28485         if(this.swallowKeys){
28486             this.field.el.swallowEvent(['keydown','keypress']);
28487         }
28488         this.field.show();
28489         this.field.on("blur", this.onBlur, this);
28490         if(this.field.grow){
28491             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28492         }
28493     },
28494
28495     onSpecialKey : function(field, e)
28496     {
28497         //Roo.log('editor onSpecialKey');
28498         if(this.completeOnEnter && e.getKey() == e.ENTER){
28499             e.stopEvent();
28500             this.completeEdit();
28501             return;
28502         }
28503         // do not fire special key otherwise it might hide close the editor...
28504         if(e.getKey() == e.ENTER){    
28505             return;
28506         }
28507         if(this.cancelOnEsc && e.getKey() == e.ESC){
28508             this.cancelEdit();
28509             return;
28510         } 
28511         this.fireEvent('specialkey', field, e);
28512     
28513     },
28514
28515     /**
28516      * Starts the editing process and shows the editor.
28517      * @param {String/HTMLElement/Element} el The element to edit
28518      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28519       * to the innerHTML of el.
28520      */
28521     startEdit : function(el, value){
28522         if(this.editing){
28523             this.completeEdit();
28524         }
28525         this.boundEl = Roo.get(el);
28526         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28527         if(!this.rendered){
28528             this.render(this.parentEl || document.body);
28529         }
28530         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28531             return;
28532         }
28533         this.startValue = v;
28534         this.field.setValue(v);
28535         if(this.autoSize){
28536             var sz = this.boundEl.getSize();
28537             switch(this.autoSize){
28538                 case "width":
28539                 this.setSize(sz.width,  "");
28540                 break;
28541                 case "height":
28542                 this.setSize("",  sz.height);
28543                 break;
28544                 default:
28545                 this.setSize(sz.width,  sz.height);
28546             }
28547         }
28548         this.el.alignTo(this.boundEl, this.alignment);
28549         this.editing = true;
28550         if(Roo.QuickTips){
28551             Roo.QuickTips.disable();
28552         }
28553         this.show();
28554     },
28555
28556     /**
28557      * Sets the height and width of this editor.
28558      * @param {Number} width The new width
28559      * @param {Number} height The new height
28560      */
28561     setSize : function(w, h){
28562         this.field.setSize(w, h);
28563         if(this.el){
28564             this.el.sync();
28565         }
28566     },
28567
28568     /**
28569      * Realigns the editor to the bound field based on the current alignment config value.
28570      */
28571     realign : function(){
28572         this.el.alignTo(this.boundEl, this.alignment);
28573     },
28574
28575     /**
28576      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28577      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28578      */
28579     completeEdit : function(remainVisible){
28580         if(!this.editing){
28581             return;
28582         }
28583         var v = this.getValue();
28584         if(this.revertInvalid !== false && !this.field.isValid()){
28585             v = this.startValue;
28586             this.cancelEdit(true);
28587         }
28588         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28589             this.editing = false;
28590             this.hide();
28591             return;
28592         }
28593         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28594             this.editing = false;
28595             if(this.updateEl && this.boundEl){
28596                 this.boundEl.update(v);
28597             }
28598             if(remainVisible !== true){
28599                 this.hide();
28600             }
28601             this.fireEvent("complete", this, v, this.startValue);
28602         }
28603     },
28604
28605     // private
28606     onShow : function(){
28607         this.el.show();
28608         if(this.hideEl !== false){
28609             this.boundEl.hide();
28610         }
28611         this.field.show();
28612         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28613             this.fixIEFocus = true;
28614             this.deferredFocus.defer(50, this);
28615         }else{
28616             this.field.focus();
28617         }
28618         this.fireEvent("startedit", this.boundEl, this.startValue);
28619     },
28620
28621     deferredFocus : function(){
28622         if(this.editing){
28623             this.field.focus();
28624         }
28625     },
28626
28627     /**
28628      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28629      * reverted to the original starting value.
28630      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28631      * cancel (defaults to false)
28632      */
28633     cancelEdit : function(remainVisible){
28634         if(this.editing){
28635             this.setValue(this.startValue);
28636             if(remainVisible !== true){
28637                 this.hide();
28638             }
28639         }
28640     },
28641
28642     // private
28643     onBlur : function(){
28644         if(this.allowBlur !== true && this.editing){
28645             this.completeEdit();
28646         }
28647     },
28648
28649     // private
28650     onHide : function(){
28651         if(this.editing){
28652             this.completeEdit();
28653             return;
28654         }
28655         this.field.blur();
28656         if(this.field.collapse){
28657             this.field.collapse();
28658         }
28659         this.el.hide();
28660         if(this.hideEl !== false){
28661             this.boundEl.show();
28662         }
28663         if(Roo.QuickTips){
28664             Roo.QuickTips.enable();
28665         }
28666     },
28667
28668     /**
28669      * Sets the data value of the editor
28670      * @param {Mixed} value Any valid value supported by the underlying field
28671      */
28672     setValue : function(v){
28673         this.field.setValue(v);
28674     },
28675
28676     /**
28677      * Gets the data value of the editor
28678      * @return {Mixed} The data value
28679      */
28680     getValue : function(){
28681         return this.field.getValue();
28682     }
28683 });/*
28684  * Based on:
28685  * Ext JS Library 1.1.1
28686  * Copyright(c) 2006-2007, Ext JS, LLC.
28687  *
28688  * Originally Released Under LGPL - original licence link has changed is not relivant.
28689  *
28690  * Fork - LGPL
28691  * <script type="text/javascript">
28692  */
28693  
28694 /**
28695  * @class Roo.BasicDialog
28696  * @extends Roo.util.Observable
28697  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28698  * <pre><code>
28699 var dlg = new Roo.BasicDialog("my-dlg", {
28700     height: 200,
28701     width: 300,
28702     minHeight: 100,
28703     minWidth: 150,
28704     modal: true,
28705     proxyDrag: true,
28706     shadow: true
28707 });
28708 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28709 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28710 dlg.addButton('Cancel', dlg.hide, dlg);
28711 dlg.show();
28712 </code></pre>
28713   <b>A Dialog should always be a direct child of the body element.</b>
28714  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28715  * @cfg {String} title Default text to display in the title bar (defaults to null)
28716  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28717  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28718  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28719  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28720  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28721  * (defaults to null with no animation)
28722  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28723  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28724  * property for valid values (defaults to 'all')
28725  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28726  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28727  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28728  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28729  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28730  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28731  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28732  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28733  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28734  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28735  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28736  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28737  * draggable = true (defaults to false)
28738  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28739  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28740  * shadow (defaults to false)
28741  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28742  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28743  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28744  * @cfg {Array} buttons Array of buttons
28745  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28746  * @constructor
28747  * Create a new BasicDialog.
28748  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28749  * @param {Object} config Configuration options
28750  */
28751 Roo.BasicDialog = function(el, config){
28752     this.el = Roo.get(el);
28753     var dh = Roo.DomHelper;
28754     if(!this.el && config && config.autoCreate){
28755         if(typeof config.autoCreate == "object"){
28756             if(!config.autoCreate.id){
28757                 config.autoCreate.id = el;
28758             }
28759             this.el = dh.append(document.body,
28760                         config.autoCreate, true);
28761         }else{
28762             this.el = dh.append(document.body,
28763                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28764         }
28765     }
28766     el = this.el;
28767     el.setDisplayed(true);
28768     el.hide = this.hideAction;
28769     this.id = el.id;
28770     el.addClass("x-dlg");
28771
28772     Roo.apply(this, config);
28773
28774     this.proxy = el.createProxy("x-dlg-proxy");
28775     this.proxy.hide = this.hideAction;
28776     this.proxy.setOpacity(.5);
28777     this.proxy.hide();
28778
28779     if(config.width){
28780         el.setWidth(config.width);
28781     }
28782     if(config.height){
28783         el.setHeight(config.height);
28784     }
28785     this.size = el.getSize();
28786     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28787         this.xy = [config.x,config.y];
28788     }else{
28789         this.xy = el.getCenterXY(true);
28790     }
28791     /** The header element @type Roo.Element */
28792     this.header = el.child("> .x-dlg-hd");
28793     /** The body element @type Roo.Element */
28794     this.body = el.child("> .x-dlg-bd");
28795     /** The footer element @type Roo.Element */
28796     this.footer = el.child("> .x-dlg-ft");
28797
28798     if(!this.header){
28799         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28800     }
28801     if(!this.body){
28802         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28803     }
28804
28805     this.header.unselectable();
28806     if(this.title){
28807         this.header.update(this.title);
28808     }
28809     // this element allows the dialog to be focused for keyboard event
28810     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28811     this.focusEl.swallowEvent("click", true);
28812
28813     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28814
28815     // wrap the body and footer for special rendering
28816     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28817     if(this.footer){
28818         this.bwrap.dom.appendChild(this.footer.dom);
28819     }
28820
28821     this.bg = this.el.createChild({
28822         tag: "div", cls:"x-dlg-bg",
28823         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28824     });
28825     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28826
28827
28828     if(this.autoScroll !== false && !this.autoTabs){
28829         this.body.setStyle("overflow", "auto");
28830     }
28831
28832     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28833
28834     if(this.closable !== false){
28835         this.el.addClass("x-dlg-closable");
28836         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28837         this.close.on("click", this.closeClick, this);
28838         this.close.addClassOnOver("x-dlg-close-over");
28839     }
28840     if(this.collapsible !== false){
28841         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28842         this.collapseBtn.on("click", this.collapseClick, this);
28843         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28844         this.header.on("dblclick", this.collapseClick, this);
28845     }
28846     if(this.resizable !== false){
28847         this.el.addClass("x-dlg-resizable");
28848         this.resizer = new Roo.Resizable(el, {
28849             minWidth: this.minWidth || 80,
28850             minHeight:this.minHeight || 80,
28851             handles: this.resizeHandles || "all",
28852             pinned: true
28853         });
28854         this.resizer.on("beforeresize", this.beforeResize, this);
28855         this.resizer.on("resize", this.onResize, this);
28856     }
28857     if(this.draggable !== false){
28858         el.addClass("x-dlg-draggable");
28859         if (!this.proxyDrag) {
28860             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28861         }
28862         else {
28863             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28864         }
28865         dd.setHandleElId(this.header.id);
28866         dd.endDrag = this.endMove.createDelegate(this);
28867         dd.startDrag = this.startMove.createDelegate(this);
28868         dd.onDrag = this.onDrag.createDelegate(this);
28869         dd.scroll = false;
28870         this.dd = dd;
28871     }
28872     if(this.modal){
28873         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28874         this.mask.enableDisplayMode("block");
28875         this.mask.hide();
28876         this.el.addClass("x-dlg-modal");
28877     }
28878     if(this.shadow){
28879         this.shadow = new Roo.Shadow({
28880             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28881             offset : this.shadowOffset
28882         });
28883     }else{
28884         this.shadowOffset = 0;
28885     }
28886     if(Roo.useShims && this.shim !== false){
28887         this.shim = this.el.createShim();
28888         this.shim.hide = this.hideAction;
28889         this.shim.hide();
28890     }else{
28891         this.shim = false;
28892     }
28893     if(this.autoTabs){
28894         this.initTabs();
28895     }
28896     if (this.buttons) { 
28897         var bts= this.buttons;
28898         this.buttons = [];
28899         Roo.each(bts, function(b) {
28900             this.addButton(b);
28901         }, this);
28902     }
28903     
28904     
28905     this.addEvents({
28906         /**
28907          * @event keydown
28908          * Fires when a key is pressed
28909          * @param {Roo.BasicDialog} this
28910          * @param {Roo.EventObject} e
28911          */
28912         "keydown" : true,
28913         /**
28914          * @event move
28915          * Fires when this dialog is moved by the user.
28916          * @param {Roo.BasicDialog} this
28917          * @param {Number} x The new page X
28918          * @param {Number} y The new page Y
28919          */
28920         "move" : true,
28921         /**
28922          * @event resize
28923          * Fires when this dialog is resized by the user.
28924          * @param {Roo.BasicDialog} this
28925          * @param {Number} width The new width
28926          * @param {Number} height The new height
28927          */
28928         "resize" : true,
28929         /**
28930          * @event beforehide
28931          * Fires before this dialog is hidden.
28932          * @param {Roo.BasicDialog} this
28933          */
28934         "beforehide" : true,
28935         /**
28936          * @event hide
28937          * Fires when this dialog is hidden.
28938          * @param {Roo.BasicDialog} this
28939          */
28940         "hide" : true,
28941         /**
28942          * @event beforeshow
28943          * Fires before this dialog is shown.
28944          * @param {Roo.BasicDialog} this
28945          */
28946         "beforeshow" : true,
28947         /**
28948          * @event show
28949          * Fires when this dialog is shown.
28950          * @param {Roo.BasicDialog} this
28951          */
28952         "show" : true
28953     });
28954     el.on("keydown", this.onKeyDown, this);
28955     el.on("mousedown", this.toFront, this);
28956     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28957     this.el.hide();
28958     Roo.DialogManager.register(this);
28959     Roo.BasicDialog.superclass.constructor.call(this);
28960 };
28961
28962 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28963     shadowOffset: Roo.isIE ? 6 : 5,
28964     minHeight: 80,
28965     minWidth: 200,
28966     minButtonWidth: 75,
28967     defaultButton: null,
28968     buttonAlign: "right",
28969     tabTag: 'div',
28970     firstShow: true,
28971
28972     /**
28973      * Sets the dialog title text
28974      * @param {String} text The title text to display
28975      * @return {Roo.BasicDialog} this
28976      */
28977     setTitle : function(text){
28978         this.header.update(text);
28979         return this;
28980     },
28981
28982     // private
28983     closeClick : function(){
28984         this.hide();
28985     },
28986
28987     // private
28988     collapseClick : function(){
28989         this[this.collapsed ? "expand" : "collapse"]();
28990     },
28991
28992     /**
28993      * Collapses the dialog to its minimized state (only the title bar is visible).
28994      * Equivalent to the user clicking the collapse dialog button.
28995      */
28996     collapse : function(){
28997         if(!this.collapsed){
28998             this.collapsed = true;
28999             this.el.addClass("x-dlg-collapsed");
29000             this.restoreHeight = this.el.getHeight();
29001             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29002         }
29003     },
29004
29005     /**
29006      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29007      * clicking the expand dialog button.
29008      */
29009     expand : function(){
29010         if(this.collapsed){
29011             this.collapsed = false;
29012             this.el.removeClass("x-dlg-collapsed");
29013             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29014         }
29015     },
29016
29017     /**
29018      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29019      * @return {Roo.TabPanel} The tabs component
29020      */
29021     initTabs : function(){
29022         var tabs = this.getTabs();
29023         while(tabs.getTab(0)){
29024             tabs.removeTab(0);
29025         }
29026         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29027             var dom = el.dom;
29028             tabs.addTab(Roo.id(dom), dom.title);
29029             dom.title = "";
29030         });
29031         tabs.activate(0);
29032         return tabs;
29033     },
29034
29035     // private
29036     beforeResize : function(){
29037         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29038     },
29039
29040     // private
29041     onResize : function(){
29042         this.refreshSize();
29043         this.syncBodyHeight();
29044         this.adjustAssets();
29045         this.focus();
29046         this.fireEvent("resize", this, this.size.width, this.size.height);
29047     },
29048
29049     // private
29050     onKeyDown : function(e){
29051         if(this.isVisible()){
29052             this.fireEvent("keydown", this, e);
29053         }
29054     },
29055
29056     /**
29057      * Resizes the dialog.
29058      * @param {Number} width
29059      * @param {Number} height
29060      * @return {Roo.BasicDialog} this
29061      */
29062     resizeTo : function(width, height){
29063         this.el.setSize(width, height);
29064         this.size = {width: width, height: height};
29065         this.syncBodyHeight();
29066         if(this.fixedcenter){
29067             this.center();
29068         }
29069         if(this.isVisible()){
29070             this.constrainXY();
29071             this.adjustAssets();
29072         }
29073         this.fireEvent("resize", this, width, height);
29074         return this;
29075     },
29076
29077
29078     /**
29079      * Resizes the dialog to fit the specified content size.
29080      * @param {Number} width
29081      * @param {Number} height
29082      * @return {Roo.BasicDialog} this
29083      */
29084     setContentSize : function(w, h){
29085         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29086         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29087         //if(!this.el.isBorderBox()){
29088             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29089             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29090         //}
29091         if(this.tabs){
29092             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29093             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29094         }
29095         this.resizeTo(w, h);
29096         return this;
29097     },
29098
29099     /**
29100      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29101      * executed in response to a particular key being pressed while the dialog is active.
29102      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29103      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29104      * @param {Function} fn The function to call
29105      * @param {Object} scope (optional) The scope of the function
29106      * @return {Roo.BasicDialog} this
29107      */
29108     addKeyListener : function(key, fn, scope){
29109         var keyCode, shift, ctrl, alt;
29110         if(typeof key == "object" && !(key instanceof Array)){
29111             keyCode = key["key"];
29112             shift = key["shift"];
29113             ctrl = key["ctrl"];
29114             alt = key["alt"];
29115         }else{
29116             keyCode = key;
29117         }
29118         var handler = function(dlg, e){
29119             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29120                 var k = e.getKey();
29121                 if(keyCode instanceof Array){
29122                     for(var i = 0, len = keyCode.length; i < len; i++){
29123                         if(keyCode[i] == k){
29124                           fn.call(scope || window, dlg, k, e);
29125                           return;
29126                         }
29127                     }
29128                 }else{
29129                     if(k == keyCode){
29130                         fn.call(scope || window, dlg, k, e);
29131                     }
29132                 }
29133             }
29134         };
29135         this.on("keydown", handler);
29136         return this;
29137     },
29138
29139     /**
29140      * Returns the TabPanel component (creates it if it doesn't exist).
29141      * Note: If you wish to simply check for the existence of tabs without creating them,
29142      * check for a null 'tabs' property.
29143      * @return {Roo.TabPanel} The tabs component
29144      */
29145     getTabs : function(){
29146         if(!this.tabs){
29147             this.el.addClass("x-dlg-auto-tabs");
29148             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29149             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29150         }
29151         return this.tabs;
29152     },
29153
29154     /**
29155      * Adds a button to the footer section of the dialog.
29156      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29157      * object or a valid Roo.DomHelper element config
29158      * @param {Function} handler The function called when the button is clicked
29159      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29160      * @return {Roo.Button} The new button
29161      */
29162     addButton : function(config, handler, scope){
29163         var dh = Roo.DomHelper;
29164         if(!this.footer){
29165             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29166         }
29167         if(!this.btnContainer){
29168             var tb = this.footer.createChild({
29169
29170                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29171                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29172             }, null, true);
29173             this.btnContainer = tb.firstChild.firstChild.firstChild;
29174         }
29175         var bconfig = {
29176             handler: handler,
29177             scope: scope,
29178             minWidth: this.minButtonWidth,
29179             hideParent:true
29180         };
29181         if(typeof config == "string"){
29182             bconfig.text = config;
29183         }else{
29184             if(config.tag){
29185                 bconfig.dhconfig = config;
29186             }else{
29187                 Roo.apply(bconfig, config);
29188             }
29189         }
29190         var fc = false;
29191         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29192             bconfig.position = Math.max(0, bconfig.position);
29193             fc = this.btnContainer.childNodes[bconfig.position];
29194         }
29195          
29196         var btn = new Roo.Button(
29197             fc ? 
29198                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29199                 : this.btnContainer.appendChild(document.createElement("td")),
29200             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29201             bconfig
29202         );
29203         this.syncBodyHeight();
29204         if(!this.buttons){
29205             /**
29206              * Array of all the buttons that have been added to this dialog via addButton
29207              * @type Array
29208              */
29209             this.buttons = [];
29210         }
29211         this.buttons.push(btn);
29212         return btn;
29213     },
29214
29215     /**
29216      * Sets the default button to be focused when the dialog is displayed.
29217      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29218      * @return {Roo.BasicDialog} this
29219      */
29220     setDefaultButton : function(btn){
29221         this.defaultButton = btn;
29222         return this;
29223     },
29224
29225     // private
29226     getHeaderFooterHeight : function(safe){
29227         var height = 0;
29228         if(this.header){
29229            height += this.header.getHeight();
29230         }
29231         if(this.footer){
29232            var fm = this.footer.getMargins();
29233             height += (this.footer.getHeight()+fm.top+fm.bottom);
29234         }
29235         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29236         height += this.centerBg.getPadding("tb");
29237         return height;
29238     },
29239
29240     // private
29241     syncBodyHeight : function(){
29242         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29243         var height = this.size.height - this.getHeaderFooterHeight(false);
29244         bd.setHeight(height-bd.getMargins("tb"));
29245         var hh = this.header.getHeight();
29246         var h = this.size.height-hh;
29247         cb.setHeight(h);
29248         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29249         bw.setHeight(h-cb.getPadding("tb"));
29250         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29251         bd.setWidth(bw.getWidth(true));
29252         if(this.tabs){
29253             this.tabs.syncHeight();
29254             if(Roo.isIE){
29255                 this.tabs.el.repaint();
29256             }
29257         }
29258     },
29259
29260     /**
29261      * Restores the previous state of the dialog if Roo.state is configured.
29262      * @return {Roo.BasicDialog} this
29263      */
29264     restoreState : function(){
29265         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29266         if(box && box.width){
29267             this.xy = [box.x, box.y];
29268             this.resizeTo(box.width, box.height);
29269         }
29270         return this;
29271     },
29272
29273     // private
29274     beforeShow : function(){
29275         this.expand();
29276         if(this.fixedcenter){
29277             this.xy = this.el.getCenterXY(true);
29278         }
29279         if(this.modal){
29280             Roo.get(document.body).addClass("x-body-masked");
29281             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29282             this.mask.show();
29283         }
29284         this.constrainXY();
29285     },
29286
29287     // private
29288     animShow : function(){
29289         var b = Roo.get(this.animateTarget).getBox();
29290         this.proxy.setSize(b.width, b.height);
29291         this.proxy.setLocation(b.x, b.y);
29292         this.proxy.show();
29293         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29294                     true, .35, this.showEl.createDelegate(this));
29295     },
29296
29297     /**
29298      * Shows the dialog.
29299      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29300      * @return {Roo.BasicDialog} this
29301      */
29302     show : function(animateTarget){
29303         if (this.fireEvent("beforeshow", this) === false){
29304             return;
29305         }
29306         if(this.syncHeightBeforeShow){
29307             this.syncBodyHeight();
29308         }else if(this.firstShow){
29309             this.firstShow = false;
29310             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29311         }
29312         this.animateTarget = animateTarget || this.animateTarget;
29313         if(!this.el.isVisible()){
29314             this.beforeShow();
29315             if(this.animateTarget && Roo.get(this.animateTarget)){
29316                 this.animShow();
29317             }else{
29318                 this.showEl();
29319             }
29320         }
29321         return this;
29322     },
29323
29324     // private
29325     showEl : function(){
29326         this.proxy.hide();
29327         this.el.setXY(this.xy);
29328         this.el.show();
29329         this.adjustAssets(true);
29330         this.toFront();
29331         this.focus();
29332         // IE peekaboo bug - fix found by Dave Fenwick
29333         if(Roo.isIE){
29334             this.el.repaint();
29335         }
29336         this.fireEvent("show", this);
29337     },
29338
29339     /**
29340      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29341      * dialog itself will receive focus.
29342      */
29343     focus : function(){
29344         if(this.defaultButton){
29345             this.defaultButton.focus();
29346         }else{
29347             this.focusEl.focus();
29348         }
29349     },
29350
29351     // private
29352     constrainXY : function(){
29353         if(this.constraintoviewport !== false){
29354             if(!this.viewSize){
29355                 if(this.container){
29356                     var s = this.container.getSize();
29357                     this.viewSize = [s.width, s.height];
29358                 }else{
29359                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29360                 }
29361             }
29362             var s = Roo.get(this.container||document).getScroll();
29363
29364             var x = this.xy[0], y = this.xy[1];
29365             var w = this.size.width, h = this.size.height;
29366             var vw = this.viewSize[0], vh = this.viewSize[1];
29367             // only move it if it needs it
29368             var moved = false;
29369             // first validate right/bottom
29370             if(x + w > vw+s.left){
29371                 x = vw - w;
29372                 moved = true;
29373             }
29374             if(y + h > vh+s.top){
29375                 y = vh - h;
29376                 moved = true;
29377             }
29378             // then make sure top/left isn't negative
29379             if(x < s.left){
29380                 x = s.left;
29381                 moved = true;
29382             }
29383             if(y < s.top){
29384                 y = s.top;
29385                 moved = true;
29386             }
29387             if(moved){
29388                 // cache xy
29389                 this.xy = [x, y];
29390                 if(this.isVisible()){
29391                     this.el.setLocation(x, y);
29392                     this.adjustAssets();
29393                 }
29394             }
29395         }
29396     },
29397
29398     // private
29399     onDrag : function(){
29400         if(!this.proxyDrag){
29401             this.xy = this.el.getXY();
29402             this.adjustAssets();
29403         }
29404     },
29405
29406     // private
29407     adjustAssets : function(doShow){
29408         var x = this.xy[0], y = this.xy[1];
29409         var w = this.size.width, h = this.size.height;
29410         if(doShow === true){
29411             if(this.shadow){
29412                 this.shadow.show(this.el);
29413             }
29414             if(this.shim){
29415                 this.shim.show();
29416             }
29417         }
29418         if(this.shadow && this.shadow.isVisible()){
29419             this.shadow.show(this.el);
29420         }
29421         if(this.shim && this.shim.isVisible()){
29422             this.shim.setBounds(x, y, w, h);
29423         }
29424     },
29425
29426     // private
29427     adjustViewport : function(w, h){
29428         if(!w || !h){
29429             w = Roo.lib.Dom.getViewWidth();
29430             h = Roo.lib.Dom.getViewHeight();
29431         }
29432         // cache the size
29433         this.viewSize = [w, h];
29434         if(this.modal && this.mask.isVisible()){
29435             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29436             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29437         }
29438         if(this.isVisible()){
29439             this.constrainXY();
29440         }
29441     },
29442
29443     /**
29444      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29445      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29446      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29447      */
29448     destroy : function(removeEl){
29449         if(this.isVisible()){
29450             this.animateTarget = null;
29451             this.hide();
29452         }
29453         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29454         if(this.tabs){
29455             this.tabs.destroy(removeEl);
29456         }
29457         Roo.destroy(
29458              this.shim,
29459              this.proxy,
29460              this.resizer,
29461              this.close,
29462              this.mask
29463         );
29464         if(this.dd){
29465             this.dd.unreg();
29466         }
29467         if(this.buttons){
29468            for(var i = 0, len = this.buttons.length; i < len; i++){
29469                this.buttons[i].destroy();
29470            }
29471         }
29472         this.el.removeAllListeners();
29473         if(removeEl === true){
29474             this.el.update("");
29475             this.el.remove();
29476         }
29477         Roo.DialogManager.unregister(this);
29478     },
29479
29480     // private
29481     startMove : function(){
29482         if(this.proxyDrag){
29483             this.proxy.show();
29484         }
29485         if(this.constraintoviewport !== false){
29486             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29487         }
29488     },
29489
29490     // private
29491     endMove : function(){
29492         if(!this.proxyDrag){
29493             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29494         }else{
29495             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29496             this.proxy.hide();
29497         }
29498         this.refreshSize();
29499         this.adjustAssets();
29500         this.focus();
29501         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29502     },
29503
29504     /**
29505      * Brings this dialog to the front of any other visible dialogs
29506      * @return {Roo.BasicDialog} this
29507      */
29508     toFront : function(){
29509         Roo.DialogManager.bringToFront(this);
29510         return this;
29511     },
29512
29513     /**
29514      * Sends this dialog to the back (under) of any other visible dialogs
29515      * @return {Roo.BasicDialog} this
29516      */
29517     toBack : function(){
29518         Roo.DialogManager.sendToBack(this);
29519         return this;
29520     },
29521
29522     /**
29523      * Centers this dialog in the viewport
29524      * @return {Roo.BasicDialog} this
29525      */
29526     center : function(){
29527         var xy = this.el.getCenterXY(true);
29528         this.moveTo(xy[0], xy[1]);
29529         return this;
29530     },
29531
29532     /**
29533      * Moves the dialog's top-left corner to the specified point
29534      * @param {Number} x
29535      * @param {Number} y
29536      * @return {Roo.BasicDialog} this
29537      */
29538     moveTo : function(x, y){
29539         this.xy = [x,y];
29540         if(this.isVisible()){
29541             this.el.setXY(this.xy);
29542             this.adjustAssets();
29543         }
29544         return this;
29545     },
29546
29547     /**
29548      * Aligns the dialog to the specified element
29549      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29550      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29551      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29552      * @return {Roo.BasicDialog} this
29553      */
29554     alignTo : function(element, position, offsets){
29555         this.xy = this.el.getAlignToXY(element, position, offsets);
29556         if(this.isVisible()){
29557             this.el.setXY(this.xy);
29558             this.adjustAssets();
29559         }
29560         return this;
29561     },
29562
29563     /**
29564      * Anchors an element to another element and realigns it when the window is resized.
29565      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29566      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29567      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29568      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29569      * is a number, it is used as the buffer delay (defaults to 50ms).
29570      * @return {Roo.BasicDialog} this
29571      */
29572     anchorTo : function(el, alignment, offsets, monitorScroll){
29573         var action = function(){
29574             this.alignTo(el, alignment, offsets);
29575         };
29576         Roo.EventManager.onWindowResize(action, this);
29577         var tm = typeof monitorScroll;
29578         if(tm != 'undefined'){
29579             Roo.EventManager.on(window, 'scroll', action, this,
29580                 {buffer: tm == 'number' ? monitorScroll : 50});
29581         }
29582         action.call(this);
29583         return this;
29584     },
29585
29586     /**
29587      * Returns true if the dialog is visible
29588      * @return {Boolean}
29589      */
29590     isVisible : function(){
29591         return this.el.isVisible();
29592     },
29593
29594     // private
29595     animHide : function(callback){
29596         var b = Roo.get(this.animateTarget).getBox();
29597         this.proxy.show();
29598         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29599         this.el.hide();
29600         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29601                     this.hideEl.createDelegate(this, [callback]));
29602     },
29603
29604     /**
29605      * Hides the dialog.
29606      * @param {Function} callback (optional) Function to call when the dialog is hidden
29607      * @return {Roo.BasicDialog} this
29608      */
29609     hide : function(callback){
29610         if (this.fireEvent("beforehide", this) === false){
29611             return;
29612         }
29613         if(this.shadow){
29614             this.shadow.hide();
29615         }
29616         if(this.shim) {
29617           this.shim.hide();
29618         }
29619         // sometimes animateTarget seems to get set.. causing problems...
29620         // this just double checks..
29621         if(this.animateTarget && Roo.get(this.animateTarget)) {
29622            this.animHide(callback);
29623         }else{
29624             this.el.hide();
29625             this.hideEl(callback);
29626         }
29627         return this;
29628     },
29629
29630     // private
29631     hideEl : function(callback){
29632         this.proxy.hide();
29633         if(this.modal){
29634             this.mask.hide();
29635             Roo.get(document.body).removeClass("x-body-masked");
29636         }
29637         this.fireEvent("hide", this);
29638         if(typeof callback == "function"){
29639             callback();
29640         }
29641     },
29642
29643     // private
29644     hideAction : function(){
29645         this.setLeft("-10000px");
29646         this.setTop("-10000px");
29647         this.setStyle("visibility", "hidden");
29648     },
29649
29650     // private
29651     refreshSize : function(){
29652         this.size = this.el.getSize();
29653         this.xy = this.el.getXY();
29654         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29655     },
29656
29657     // private
29658     // z-index is managed by the DialogManager and may be overwritten at any time
29659     setZIndex : function(index){
29660         if(this.modal){
29661             this.mask.setStyle("z-index", index);
29662         }
29663         if(this.shim){
29664             this.shim.setStyle("z-index", ++index);
29665         }
29666         if(this.shadow){
29667             this.shadow.setZIndex(++index);
29668         }
29669         this.el.setStyle("z-index", ++index);
29670         if(this.proxy){
29671             this.proxy.setStyle("z-index", ++index);
29672         }
29673         if(this.resizer){
29674             this.resizer.proxy.setStyle("z-index", ++index);
29675         }
29676
29677         this.lastZIndex = index;
29678     },
29679
29680     /**
29681      * Returns the element for this dialog
29682      * @return {Roo.Element} The underlying dialog Element
29683      */
29684     getEl : function(){
29685         return this.el;
29686     }
29687 });
29688
29689 /**
29690  * @class Roo.DialogManager
29691  * Provides global access to BasicDialogs that have been created and
29692  * support for z-indexing (layering) multiple open dialogs.
29693  */
29694 Roo.DialogManager = function(){
29695     var list = {};
29696     var accessList = [];
29697     var front = null;
29698
29699     // private
29700     var sortDialogs = function(d1, d2){
29701         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29702     };
29703
29704     // private
29705     var orderDialogs = function(){
29706         accessList.sort(sortDialogs);
29707         var seed = Roo.DialogManager.zseed;
29708         for(var i = 0, len = accessList.length; i < len; i++){
29709             var dlg = accessList[i];
29710             if(dlg){
29711                 dlg.setZIndex(seed + (i*10));
29712             }
29713         }
29714     };
29715
29716     return {
29717         /**
29718          * The starting z-index for BasicDialogs (defaults to 9000)
29719          * @type Number The z-index value
29720          */
29721         zseed : 9000,
29722
29723         // private
29724         register : function(dlg){
29725             list[dlg.id] = dlg;
29726             accessList.push(dlg);
29727         },
29728
29729         // private
29730         unregister : function(dlg){
29731             delete list[dlg.id];
29732             var i=0;
29733             var len=0;
29734             if(!accessList.indexOf){
29735                 for(  i = 0, len = accessList.length; i < len; i++){
29736                     if(accessList[i] == dlg){
29737                         accessList.splice(i, 1);
29738                         return;
29739                     }
29740                 }
29741             }else{
29742                  i = accessList.indexOf(dlg);
29743                 if(i != -1){
29744                     accessList.splice(i, 1);
29745                 }
29746             }
29747         },
29748
29749         /**
29750          * Gets a registered dialog by id
29751          * @param {String/Object} id The id of the dialog or a dialog
29752          * @return {Roo.BasicDialog} this
29753          */
29754         get : function(id){
29755             return typeof id == "object" ? id : list[id];
29756         },
29757
29758         /**
29759          * Brings the specified dialog to the front
29760          * @param {String/Object} dlg The id of the dialog or a dialog
29761          * @return {Roo.BasicDialog} this
29762          */
29763         bringToFront : function(dlg){
29764             dlg = this.get(dlg);
29765             if(dlg != front){
29766                 front = dlg;
29767                 dlg._lastAccess = new Date().getTime();
29768                 orderDialogs();
29769             }
29770             return dlg;
29771         },
29772
29773         /**
29774          * Sends the specified dialog to the back
29775          * @param {String/Object} dlg The id of the dialog or a dialog
29776          * @return {Roo.BasicDialog} this
29777          */
29778         sendToBack : function(dlg){
29779             dlg = this.get(dlg);
29780             dlg._lastAccess = -(new Date().getTime());
29781             orderDialogs();
29782             return dlg;
29783         },
29784
29785         /**
29786          * Hides all dialogs
29787          */
29788         hideAll : function(){
29789             for(var id in list){
29790                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29791                     list[id].hide();
29792                 }
29793             }
29794         }
29795     };
29796 }();
29797
29798 /**
29799  * @class Roo.LayoutDialog
29800  * @extends Roo.BasicDialog
29801  * Dialog which provides adjustments for working with a layout in a Dialog.
29802  * Add your necessary layout config options to the dialog's config.<br>
29803  * Example usage (including a nested layout):
29804  * <pre><code>
29805 if(!dialog){
29806     dialog = new Roo.LayoutDialog("download-dlg", {
29807         modal: true,
29808         width:600,
29809         height:450,
29810         shadow:true,
29811         minWidth:500,
29812         minHeight:350,
29813         autoTabs:true,
29814         proxyDrag:true,
29815         // layout config merges with the dialog config
29816         center:{
29817             tabPosition: "top",
29818             alwaysShowTabs: true
29819         }
29820     });
29821     dialog.addKeyListener(27, dialog.hide, dialog);
29822     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29823     dialog.addButton("Build It!", this.getDownload, this);
29824
29825     // we can even add nested layouts
29826     var innerLayout = new Roo.BorderLayout("dl-inner", {
29827         east: {
29828             initialSize: 200,
29829             autoScroll:true,
29830             split:true
29831         },
29832         center: {
29833             autoScroll:true
29834         }
29835     });
29836     innerLayout.beginUpdate();
29837     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29838     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29839     innerLayout.endUpdate(true);
29840
29841     var layout = dialog.getLayout();
29842     layout.beginUpdate();
29843     layout.add("center", new Roo.ContentPanel("standard-panel",
29844                         {title: "Download the Source", fitToFrame:true}));
29845     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29846                {title: "Build your own roo.js"}));
29847     layout.getRegion("center").showPanel(sp);
29848     layout.endUpdate();
29849 }
29850 </code></pre>
29851     * @constructor
29852     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29853     * @param {Object} config configuration options
29854   */
29855 Roo.LayoutDialog = function(el, cfg){
29856     
29857     var config=  cfg;
29858     if (typeof(cfg) == 'undefined') {
29859         config = Roo.apply({}, el);
29860         // not sure why we use documentElement here.. - it should always be body.
29861         // IE7 borks horribly if we use documentElement.
29862         // webkit also does not like documentElement - it creates a body element...
29863         el = Roo.get( document.body || document.documentElement ).createChild();
29864         //config.autoCreate = true;
29865     }
29866     
29867     
29868     config.autoTabs = false;
29869     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29870     this.body.setStyle({overflow:"hidden", position:"relative"});
29871     this.layout = new Roo.BorderLayout(this.body.dom, config);
29872     this.layout.monitorWindowResize = false;
29873     this.el.addClass("x-dlg-auto-layout");
29874     // fix case when center region overwrites center function
29875     this.center = Roo.BasicDialog.prototype.center;
29876     this.on("show", this.layout.layout, this.layout, true);
29877     if (config.items) {
29878         var xitems = config.items;
29879         delete config.items;
29880         Roo.each(xitems, this.addxtype, this);
29881     }
29882     
29883     
29884 };
29885 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29886     /**
29887      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29888      * @deprecated
29889      */
29890     endUpdate : function(){
29891         this.layout.endUpdate();
29892     },
29893
29894     /**
29895      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29896      *  @deprecated
29897      */
29898     beginUpdate : function(){
29899         this.layout.beginUpdate();
29900     },
29901
29902     /**
29903      * Get the BorderLayout for this dialog
29904      * @return {Roo.BorderLayout}
29905      */
29906     getLayout : function(){
29907         return this.layout;
29908     },
29909
29910     showEl : function(){
29911         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29912         if(Roo.isIE7){
29913             this.layout.layout();
29914         }
29915     },
29916
29917     // private
29918     // Use the syncHeightBeforeShow config option to control this automatically
29919     syncBodyHeight : function(){
29920         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29921         if(this.layout){this.layout.layout();}
29922     },
29923     
29924       /**
29925      * Add an xtype element (actually adds to the layout.)
29926      * @return {Object} xdata xtype object data.
29927      */
29928     
29929     addxtype : function(c) {
29930         return this.layout.addxtype(c);
29931     }
29932 });/*
29933  * Based on:
29934  * Ext JS Library 1.1.1
29935  * Copyright(c) 2006-2007, Ext JS, LLC.
29936  *
29937  * Originally Released Under LGPL - original licence link has changed is not relivant.
29938  *
29939  * Fork - LGPL
29940  * <script type="text/javascript">
29941  */
29942  
29943 /**
29944  * @class Roo.MessageBox
29945  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29946  * Example usage:
29947  *<pre><code>
29948 // Basic alert:
29949 Roo.Msg.alert('Status', 'Changes saved successfully.');
29950
29951 // Prompt for user data:
29952 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29953     if (btn == 'ok'){
29954         // process text value...
29955     }
29956 });
29957
29958 // Show a dialog using config options:
29959 Roo.Msg.show({
29960    title:'Save Changes?',
29961    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29962    buttons: Roo.Msg.YESNOCANCEL,
29963    fn: processResult,
29964    animEl: 'elId'
29965 });
29966 </code></pre>
29967  * @singleton
29968  */
29969 Roo.MessageBox = function(){
29970     var dlg, opt, mask, waitTimer;
29971     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29972     var buttons, activeTextEl, bwidth;
29973
29974     // private
29975     var handleButton = function(button){
29976         dlg.hide();
29977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29978     };
29979
29980     // private
29981     var handleHide = function(){
29982         if(opt && opt.cls){
29983             dlg.el.removeClass(opt.cls);
29984         }
29985         if(waitTimer){
29986             Roo.TaskMgr.stop(waitTimer);
29987             waitTimer = null;
29988         }
29989     };
29990
29991     // private
29992     var updateButtons = function(b){
29993         var width = 0;
29994         if(!b){
29995             buttons["ok"].hide();
29996             buttons["cancel"].hide();
29997             buttons["yes"].hide();
29998             buttons["no"].hide();
29999             dlg.footer.dom.style.display = 'none';
30000             return width;
30001         }
30002         dlg.footer.dom.style.display = '';
30003         for(var k in buttons){
30004             if(typeof buttons[k] != "function"){
30005                 if(b[k]){
30006                     buttons[k].show();
30007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30008                     width += buttons[k].el.getWidth()+15;
30009                 }else{
30010                     buttons[k].hide();
30011                 }
30012             }
30013         }
30014         return width;
30015     };
30016
30017     // private
30018     var handleEsc = function(d, k, e){
30019         if(opt && opt.closable !== false){
30020             dlg.hide();
30021         }
30022         if(e){
30023             e.stopEvent();
30024         }
30025     };
30026
30027     return {
30028         /**
30029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30030          * @return {Roo.BasicDialog} The BasicDialog element
30031          */
30032         getDialog : function(){
30033            if(!dlg){
30034                 dlg = new Roo.BasicDialog("x-msg-box", {
30035                     autoCreate : true,
30036                     shadow: true,
30037                     draggable: true,
30038                     resizable:false,
30039                     constraintoviewport:false,
30040                     fixedcenter:true,
30041                     collapsible : false,
30042                     shim:true,
30043                     modal: true,
30044                     width:400, height:100,
30045                     buttonAlign:"center",
30046                     closeClick : function(){
30047                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30048                             handleButton("no");
30049                         }else{
30050                             handleButton("cancel");
30051                         }
30052                     }
30053                 });
30054                 dlg.on("hide", handleHide);
30055                 mask = dlg.mask;
30056                 dlg.addKeyListener(27, handleEsc);
30057                 buttons = {};
30058                 var bt = this.buttonText;
30059                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30060                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30061                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30062                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30063                 bodyEl = dlg.body.createChild({
30064
30065                     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>'
30066                 });
30067                 msgEl = bodyEl.dom.firstChild;
30068                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30069                 textboxEl.enableDisplayMode();
30070                 textboxEl.addKeyListener([10,13], function(){
30071                     if(dlg.isVisible() && opt && opt.buttons){
30072                         if(opt.buttons.ok){
30073                             handleButton("ok");
30074                         }else if(opt.buttons.yes){
30075                             handleButton("yes");
30076                         }
30077                     }
30078                 });
30079                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30080                 textareaEl.enableDisplayMode();
30081                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30082                 progressEl.enableDisplayMode();
30083                 var pf = progressEl.dom.firstChild;
30084                 if (pf) {
30085                     pp = Roo.get(pf.firstChild);
30086                     pp.setHeight(pf.offsetHeight);
30087                 }
30088                 
30089             }
30090             return dlg;
30091         },
30092
30093         /**
30094          * Updates the message box body text
30095          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30096          * the XHTML-compliant non-breaking space character '&amp;#160;')
30097          * @return {Roo.MessageBox} This message box
30098          */
30099         updateText : function(text){
30100             if(!dlg.isVisible() && !opt.width){
30101                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30102             }
30103             msgEl.innerHTML = text || '&#160;';
30104             this.updateTextSize.defer(100, this);
30105             return this;
30106         },
30107         
30108         updateTextSize: function()
30109         {
30110         
30111             var cw =  Math.max(msgEl.offsetWidth, msgEl.scrollWidth);
30112             Roo.log("guesed size: " + cw);
30113             var w = Math.max(
30114                     Math.min(opt.width || cw , this.maxWidth), 
30115                     Math.max(opt.minWidth || this.minWidth, bwidth)
30116             );
30117             if(opt.prompt){
30118                 activeTextEl.setWidth(w);
30119             }
30120             if(dlg.isVisible()){
30121                 dlg.fixedcenter = false;
30122             }
30123             // to big, make it scoll.
30124             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30125                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30126                 bodyEl.dom.style.overflowY = 'auto !important';
30127             } else {
30128                 bodyEl.dom.style.height = '';
30129                 bodyEl.dom.style.overflowY = '';
30130             }
30131             if (cw > w) {
30132                 bodyEl.dom.style.overflowX = 'auto !important';
30133             } else {
30134                 bodyEl.dom.style.overflowX = '';
30135             }
30136             
30137             dlg.setContentSize(w, bodyEl.getHeight());
30138             if(dlg.isVisible()){
30139                 dlg.fixedcenter = true;
30140             }
30141             return this;
30142         },
30143
30144         /**
30145          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30146          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30147          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30148          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30149          * @return {Roo.MessageBox} This message box
30150          */
30151         updateProgress : function(value, text){
30152             if(text){
30153                 this.updateText(text);
30154             }
30155             if (pp) { // weird bug on my firefox - for some reason this is not defined
30156                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30157             }
30158             return this;
30159         },        
30160
30161         /**
30162          * Returns true if the message box is currently displayed
30163          * @return {Boolean} True if the message box is visible, else false
30164          */
30165         isVisible : function(){
30166             return dlg && dlg.isVisible();  
30167         },
30168
30169         /**
30170          * Hides the message box if it is displayed
30171          */
30172         hide : function(){
30173             if(this.isVisible()){
30174                 dlg.hide();
30175             }  
30176         },
30177
30178         /**
30179          * Displays a new message box, or reinitializes an existing message box, based on the config options
30180          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30181          * The following config object properties are supported:
30182          * <pre>
30183 Property    Type             Description
30184 ----------  ---------------  ------------------------------------------------------------------------------------
30185 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30186                                    closes (defaults to undefined)
30187 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30188                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30189 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30190                                    progress and wait dialogs will ignore this property and always hide the
30191                                    close button as they can only be closed programmatically.
30192 cls               String           A custom CSS class to apply to the message box element
30193 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30194                                    displayed (defaults to 75)
30195 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30196                                    function will be btn (the name of the button that was clicked, if applicable,
30197                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30198                                    Progress and wait dialogs will ignore this option since they do not respond to
30199                                    user actions and can only be closed programmatically, so any required function
30200                                    should be called by the same code after it closes the dialog.
30201 icon              String           A CSS class that provides a background image to be used as an icon for
30202                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30203 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30204 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30205 modal             Boolean          False to allow user interaction with the page while the message box is
30206                                    displayed (defaults to true)
30207 msg               String           A string that will replace the existing message box body text (defaults
30208                                    to the XHTML-compliant non-breaking space character '&#160;')
30209 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30210 progress          Boolean          True to display a progress bar (defaults to false)
30211 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30212 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30213 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30214 title             String           The title text
30215 value             String           The string value to set into the active textbox element if displayed
30216 wait              Boolean          True to display a progress bar (defaults to false)
30217 width             Number           The width of the dialog in pixels
30218 </pre>
30219          *
30220          * Example usage:
30221          * <pre><code>
30222 Roo.Msg.show({
30223    title: 'Address',
30224    msg: 'Please enter your address:',
30225    width: 300,
30226    buttons: Roo.MessageBox.OKCANCEL,
30227    multiline: true,
30228    fn: saveAddress,
30229    animEl: 'addAddressBtn'
30230 });
30231 </code></pre>
30232          * @param {Object} config Configuration options
30233          * @return {Roo.MessageBox} This message box
30234          */
30235         show : function(options)
30236         {
30237             
30238             // this causes nightmares if you show one dialog after another
30239             // especially on callbacks..
30240              
30241             if(this.isVisible()){
30242                 
30243                 this.hide();
30244                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30245                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30246                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30247                 
30248             }
30249             var d = this.getDialog();
30250             opt = options;
30251             d.setTitle(opt.title || "&#160;");
30252             d.close.setDisplayed(opt.closable !== false);
30253             activeTextEl = textboxEl;
30254             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30255             if(opt.prompt){
30256                 if(opt.multiline){
30257                     textboxEl.hide();
30258                     textareaEl.show();
30259                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30260                         opt.multiline : this.defaultTextHeight);
30261                     activeTextEl = textareaEl;
30262                 }else{
30263                     textboxEl.show();
30264                     textareaEl.hide();
30265                 }
30266             }else{
30267                 textboxEl.hide();
30268                 textareaEl.hide();
30269             }
30270             progressEl.setDisplayed(opt.progress === true);
30271             this.updateProgress(0);
30272             activeTextEl.dom.value = opt.value || "";
30273             if(opt.prompt){
30274                 dlg.setDefaultButton(activeTextEl);
30275             }else{
30276                 var bs = opt.buttons;
30277                 var db = null;
30278                 if(bs && bs.ok){
30279                     db = buttons["ok"];
30280                 }else if(bs && bs.yes){
30281                     db = buttons["yes"];
30282                 }
30283                 dlg.setDefaultButton(db);
30284             }
30285             bwidth = updateButtons(opt.buttons);
30286             this.updateText(opt.msg);
30287             if(opt.cls){
30288                 d.el.addClass(opt.cls);
30289             }
30290             d.proxyDrag = opt.proxyDrag === true;
30291             d.modal = opt.modal !== false;
30292             d.mask = opt.modal !== false ? mask : false;
30293             if(!d.isVisible()){
30294                 // force it to the end of the z-index stack so it gets a cursor in FF
30295                 document.body.appendChild(dlg.el.dom);
30296                 d.animateTarget = null;
30297                 d.show(options.animEl);
30298             }
30299             return this;
30300         },
30301
30302         /**
30303          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30304          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30305          * and closing the message box when the process is complete.
30306          * @param {String} title The title bar text
30307          * @param {String} msg The message box body text
30308          * @return {Roo.MessageBox} This message box
30309          */
30310         progress : function(title, msg){
30311             this.show({
30312                 title : title,
30313                 msg : msg,
30314                 buttons: false,
30315                 progress:true,
30316                 closable:false,
30317                 minWidth: this.minProgressWidth,
30318                 modal : true
30319             });
30320             return this;
30321         },
30322
30323         /**
30324          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30325          * If a callback function is passed it will be called after the user clicks the button, and the
30326          * id of the button that was clicked will be passed as the only parameter to the callback
30327          * (could also be the top-right close button).
30328          * @param {String} title The title bar text
30329          * @param {String} msg The message box body text
30330          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30331          * @param {Object} scope (optional) The scope of the callback function
30332          * @return {Roo.MessageBox} This message box
30333          */
30334         alert : function(title, msg, fn, scope){
30335             this.show({
30336                 title : title,
30337                 msg : msg,
30338                 buttons: this.OK,
30339                 fn: fn,
30340                 scope : scope,
30341                 modal : true
30342             });
30343             return this;
30344         },
30345
30346         /**
30347          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30348          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30349          * You are responsible for closing the message box when the process is complete.
30350          * @param {String} msg The message box body text
30351          * @param {String} title (optional) The title bar text
30352          * @return {Roo.MessageBox} This message box
30353          */
30354         wait : function(msg, title){
30355             this.show({
30356                 title : title,
30357                 msg : msg,
30358                 buttons: false,
30359                 closable:false,
30360                 progress:true,
30361                 modal:true,
30362                 width:300,
30363                 wait:true
30364             });
30365             waitTimer = Roo.TaskMgr.start({
30366                 run: function(i){
30367                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30368                 },
30369                 interval: 1000
30370             });
30371             return this;
30372         },
30373
30374         /**
30375          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30376          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30377          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30378          * @param {String} title The title bar text
30379          * @param {String} msg The message box body text
30380          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30381          * @param {Object} scope (optional) The scope of the callback function
30382          * @return {Roo.MessageBox} This message box
30383          */
30384         confirm : function(title, msg, fn, scope){
30385             this.show({
30386                 title : title,
30387                 msg : msg,
30388                 buttons: this.YESNO,
30389                 fn: fn,
30390                 scope : scope,
30391                 modal : true
30392             });
30393             return this;
30394         },
30395
30396         /**
30397          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30398          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30399          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30400          * (could also be the top-right close button) and the text that was entered will be passed as the two
30401          * parameters to the callback.
30402          * @param {String} title The title bar text
30403          * @param {String} msg The message box body text
30404          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30405          * @param {Object} scope (optional) The scope of the callback function
30406          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30407          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30408          * @return {Roo.MessageBox} This message box
30409          */
30410         prompt : function(title, msg, fn, scope, multiline){
30411             this.show({
30412                 title : title,
30413                 msg : msg,
30414                 buttons: this.OKCANCEL,
30415                 fn: fn,
30416                 minWidth:250,
30417                 scope : scope,
30418                 prompt:true,
30419                 multiline: multiline,
30420                 modal : true
30421             });
30422             return this;
30423         },
30424
30425         /**
30426          * Button config that displays a single OK button
30427          * @type Object
30428          */
30429         OK : {ok:true},
30430         /**
30431          * Button config that displays Yes and No buttons
30432          * @type Object
30433          */
30434         YESNO : {yes:true, no:true},
30435         /**
30436          * Button config that displays OK and Cancel buttons
30437          * @type Object
30438          */
30439         OKCANCEL : {ok:true, cancel:true},
30440         /**
30441          * Button config that displays Yes, No and Cancel buttons
30442          * @type Object
30443          */
30444         YESNOCANCEL : {yes:true, no:true, cancel:true},
30445
30446         /**
30447          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30448          * @type Number
30449          */
30450         defaultTextHeight : 75,
30451         /**
30452          * The maximum width in pixels of the message box (defaults to 600)
30453          * @type Number
30454          */
30455         maxWidth : 600,
30456         /**
30457          * The minimum width in pixels of the message box (defaults to 100)
30458          * @type Number
30459          */
30460         minWidth : 100,
30461         /**
30462          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30463          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30464          * @type Number
30465          */
30466         minProgressWidth : 250,
30467         /**
30468          * An object containing the default button text strings that can be overriden for localized language support.
30469          * Supported properties are: ok, cancel, yes and no.
30470          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30471          * @type Object
30472          */
30473         buttonText : {
30474             ok : "OK",
30475             cancel : "Cancel",
30476             yes : "Yes",
30477             no : "No"
30478         }
30479     };
30480 }();
30481
30482 /**
30483  * Shorthand for {@link Roo.MessageBox}
30484  */
30485 Roo.Msg = Roo.MessageBox;/*
30486  * Based on:
30487  * Ext JS Library 1.1.1
30488  * Copyright(c) 2006-2007, Ext JS, LLC.
30489  *
30490  * Originally Released Under LGPL - original licence link has changed is not relivant.
30491  *
30492  * Fork - LGPL
30493  * <script type="text/javascript">
30494  */
30495 /**
30496  * @class Roo.QuickTips
30497  * Provides attractive and customizable tooltips for any element.
30498  * @singleton
30499  */
30500 Roo.QuickTips = function(){
30501     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30502     var ce, bd, xy, dd;
30503     var visible = false, disabled = true, inited = false;
30504     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30505     
30506     var onOver = function(e){
30507         if(disabled){
30508             return;
30509         }
30510         var t = e.getTarget();
30511         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30512             return;
30513         }
30514         if(ce && t == ce.el){
30515             clearTimeout(hideProc);
30516             return;
30517         }
30518         if(t && tagEls[t.id]){
30519             tagEls[t.id].el = t;
30520             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30521             return;
30522         }
30523         var ttp, et = Roo.fly(t);
30524         var ns = cfg.namespace;
30525         if(tm.interceptTitles && t.title){
30526             ttp = t.title;
30527             t.qtip = ttp;
30528             t.removeAttribute("title");
30529             e.preventDefault();
30530         }else{
30531             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30532         }
30533         if(ttp){
30534             showProc = show.defer(tm.showDelay, tm, [{
30535                 el: t, 
30536                 text: ttp, 
30537                 width: et.getAttributeNS(ns, cfg.width),
30538                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30539                 title: et.getAttributeNS(ns, cfg.title),
30540                     cls: et.getAttributeNS(ns, cfg.cls)
30541             }]);
30542         }
30543     };
30544     
30545     var onOut = function(e){
30546         clearTimeout(showProc);
30547         var t = e.getTarget();
30548         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30549             hideProc = setTimeout(hide, tm.hideDelay);
30550         }
30551     };
30552     
30553     var onMove = function(e){
30554         if(disabled){
30555             return;
30556         }
30557         xy = e.getXY();
30558         xy[1] += 18;
30559         if(tm.trackMouse && ce){
30560             el.setXY(xy);
30561         }
30562     };
30563     
30564     var onDown = function(e){
30565         clearTimeout(showProc);
30566         clearTimeout(hideProc);
30567         if(!e.within(el)){
30568             if(tm.hideOnClick){
30569                 hide();
30570                 tm.disable();
30571                 tm.enable.defer(100, tm);
30572             }
30573         }
30574     };
30575     
30576     var getPad = function(){
30577         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30578     };
30579
30580     var show = function(o){
30581         if(disabled){
30582             return;
30583         }
30584         clearTimeout(dismissProc);
30585         ce = o;
30586         if(removeCls){ // in case manually hidden
30587             el.removeClass(removeCls);
30588             removeCls = null;
30589         }
30590         if(ce.cls){
30591             el.addClass(ce.cls);
30592             removeCls = ce.cls;
30593         }
30594         if(ce.title){
30595             tipTitle.update(ce.title);
30596             tipTitle.show();
30597         }else{
30598             tipTitle.update('');
30599             tipTitle.hide();
30600         }
30601         el.dom.style.width  = tm.maxWidth+'px';
30602         //tipBody.dom.style.width = '';
30603         tipBodyText.update(o.text);
30604         var p = getPad(), w = ce.width;
30605         if(!w){
30606             var td = tipBodyText.dom;
30607             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30608             if(aw > tm.maxWidth){
30609                 w = tm.maxWidth;
30610             }else if(aw < tm.minWidth){
30611                 w = tm.minWidth;
30612             }else{
30613                 w = aw;
30614             }
30615         }
30616         //tipBody.setWidth(w);
30617         el.setWidth(parseInt(w, 10) + p);
30618         if(ce.autoHide === false){
30619             close.setDisplayed(true);
30620             if(dd){
30621                 dd.unlock();
30622             }
30623         }else{
30624             close.setDisplayed(false);
30625             if(dd){
30626                 dd.lock();
30627             }
30628         }
30629         if(xy){
30630             el.avoidY = xy[1]-18;
30631             el.setXY(xy);
30632         }
30633         if(tm.animate){
30634             el.setOpacity(.1);
30635             el.setStyle("visibility", "visible");
30636             el.fadeIn({callback: afterShow});
30637         }else{
30638             afterShow();
30639         }
30640     };
30641     
30642     var afterShow = function(){
30643         if(ce){
30644             el.show();
30645             esc.enable();
30646             if(tm.autoDismiss && ce.autoHide !== false){
30647                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30648             }
30649         }
30650     };
30651     
30652     var hide = function(noanim){
30653         clearTimeout(dismissProc);
30654         clearTimeout(hideProc);
30655         ce = null;
30656         if(el.isVisible()){
30657             esc.disable();
30658             if(noanim !== true && tm.animate){
30659                 el.fadeOut({callback: afterHide});
30660             }else{
30661                 afterHide();
30662             } 
30663         }
30664     };
30665     
30666     var afterHide = function(){
30667         el.hide();
30668         if(removeCls){
30669             el.removeClass(removeCls);
30670             removeCls = null;
30671         }
30672     };
30673     
30674     return {
30675         /**
30676         * @cfg {Number} minWidth
30677         * The minimum width of the quick tip (defaults to 40)
30678         */
30679        minWidth : 40,
30680         /**
30681         * @cfg {Number} maxWidth
30682         * The maximum width of the quick tip (defaults to 300)
30683         */
30684        maxWidth : 300,
30685         /**
30686         * @cfg {Boolean} interceptTitles
30687         * True to automatically use the element's DOM title value if available (defaults to false)
30688         */
30689        interceptTitles : false,
30690         /**
30691         * @cfg {Boolean} trackMouse
30692         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30693         */
30694        trackMouse : false,
30695         /**
30696         * @cfg {Boolean} hideOnClick
30697         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30698         */
30699        hideOnClick : true,
30700         /**
30701         * @cfg {Number} showDelay
30702         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30703         */
30704        showDelay : 500,
30705         /**
30706         * @cfg {Number} hideDelay
30707         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30708         */
30709        hideDelay : 200,
30710         /**
30711         * @cfg {Boolean} autoHide
30712         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30713         * Used in conjunction with hideDelay.
30714         */
30715        autoHide : true,
30716         /**
30717         * @cfg {Boolean}
30718         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30719         * (defaults to true).  Used in conjunction with autoDismissDelay.
30720         */
30721        autoDismiss : true,
30722         /**
30723         * @cfg {Number}
30724         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30725         */
30726        autoDismissDelay : 5000,
30727        /**
30728         * @cfg {Boolean} animate
30729         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30730         */
30731        animate : false,
30732
30733        /**
30734         * @cfg {String} title
30735         * Title text to display (defaults to '').  This can be any valid HTML markup.
30736         */
30737         title: '',
30738        /**
30739         * @cfg {String} text
30740         * Body text to display (defaults to '').  This can be any valid HTML markup.
30741         */
30742         text : '',
30743        /**
30744         * @cfg {String} cls
30745         * A CSS class to apply to the base quick tip element (defaults to '').
30746         */
30747         cls : '',
30748        /**
30749         * @cfg {Number} width
30750         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30751         * minWidth or maxWidth.
30752         */
30753         width : null,
30754
30755     /**
30756      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30757      * or display QuickTips in a page.
30758      */
30759        init : function(){
30760           tm = Roo.QuickTips;
30761           cfg = tm.tagConfig;
30762           if(!inited){
30763               if(!Roo.isReady){ // allow calling of init() before onReady
30764                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30765                   return;
30766               }
30767               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30768               el.fxDefaults = {stopFx: true};
30769               // maximum custom styling
30770               //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>');
30771               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>');              
30772               tipTitle = el.child('h3');
30773               tipTitle.enableDisplayMode("block");
30774               tipBody = el.child('div.x-tip-bd');
30775               tipBodyText = el.child('div.x-tip-bd-inner');
30776               //bdLeft = el.child('div.x-tip-bd-left');
30777               //bdRight = el.child('div.x-tip-bd-right');
30778               close = el.child('div.x-tip-close');
30779               close.enableDisplayMode("block");
30780               close.on("click", hide);
30781               var d = Roo.get(document);
30782               d.on("mousedown", onDown);
30783               d.on("mouseover", onOver);
30784               d.on("mouseout", onOut);
30785               d.on("mousemove", onMove);
30786               esc = d.addKeyListener(27, hide);
30787               esc.disable();
30788               if(Roo.dd.DD){
30789                   dd = el.initDD("default", null, {
30790                       onDrag : function(){
30791                           el.sync();  
30792                       }
30793                   });
30794                   dd.setHandleElId(tipTitle.id);
30795                   dd.lock();
30796               }
30797               inited = true;
30798           }
30799           this.enable(); 
30800        },
30801
30802     /**
30803      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30804      * are supported:
30805      * <pre>
30806 Property    Type                   Description
30807 ----------  ---------------------  ------------------------------------------------------------------------
30808 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30809      * </ul>
30810      * @param {Object} config The config object
30811      */
30812        register : function(config){
30813            var cs = config instanceof Array ? config : arguments;
30814            for(var i = 0, len = cs.length; i < len; i++) {
30815                var c = cs[i];
30816                var target = c.target;
30817                if(target){
30818                    if(target instanceof Array){
30819                        for(var j = 0, jlen = target.length; j < jlen; j++){
30820                            tagEls[target[j]] = c;
30821                        }
30822                    }else{
30823                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30824                    }
30825                }
30826            }
30827        },
30828
30829     /**
30830      * Removes this quick tip from its element and destroys it.
30831      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30832      */
30833        unregister : function(el){
30834            delete tagEls[Roo.id(el)];
30835        },
30836
30837     /**
30838      * Enable this quick tip.
30839      */
30840        enable : function(){
30841            if(inited && disabled){
30842                locks.pop();
30843                if(locks.length < 1){
30844                    disabled = false;
30845                }
30846            }
30847        },
30848
30849     /**
30850      * Disable this quick tip.
30851      */
30852        disable : function(){
30853           disabled = true;
30854           clearTimeout(showProc);
30855           clearTimeout(hideProc);
30856           clearTimeout(dismissProc);
30857           if(ce){
30858               hide(true);
30859           }
30860           locks.push(1);
30861        },
30862
30863     /**
30864      * Returns true if the quick tip is enabled, else false.
30865      */
30866        isEnabled : function(){
30867             return !disabled;
30868        },
30869
30870         // private
30871        tagConfig : {
30872            namespace : "ext",
30873            attribute : "qtip",
30874            width : "width",
30875            target : "target",
30876            title : "qtitle",
30877            hide : "hide",
30878            cls : "qclass"
30879        }
30880    };
30881 }();
30882
30883 // backwards compat
30884 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30885  * Based on:
30886  * Ext JS Library 1.1.1
30887  * Copyright(c) 2006-2007, Ext JS, LLC.
30888  *
30889  * Originally Released Under LGPL - original licence link has changed is not relivant.
30890  *
30891  * Fork - LGPL
30892  * <script type="text/javascript">
30893  */
30894  
30895
30896 /**
30897  * @class Roo.tree.TreePanel
30898  * @extends Roo.data.Tree
30899
30900  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30901  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30902  * @cfg {Boolean} enableDD true to enable drag and drop
30903  * @cfg {Boolean} enableDrag true to enable just drag
30904  * @cfg {Boolean} enableDrop true to enable just drop
30905  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30906  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30907  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30908  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30909  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30910  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30911  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30912  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30913  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30914  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30915  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30916  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30917  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30918  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30919  * @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>
30920  * @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>
30921  * 
30922  * @constructor
30923  * @param {String/HTMLElement/Element} el The container element
30924  * @param {Object} config
30925  */
30926 Roo.tree.TreePanel = function(el, config){
30927     var root = false;
30928     var loader = false;
30929     if (config.root) {
30930         root = config.root;
30931         delete config.root;
30932     }
30933     if (config.loader) {
30934         loader = config.loader;
30935         delete config.loader;
30936     }
30937     
30938     Roo.apply(this, config);
30939     Roo.tree.TreePanel.superclass.constructor.call(this);
30940     this.el = Roo.get(el);
30941     this.el.addClass('x-tree');
30942     //console.log(root);
30943     if (root) {
30944         this.setRootNode( Roo.factory(root, Roo.tree));
30945     }
30946     if (loader) {
30947         this.loader = Roo.factory(loader, Roo.tree);
30948     }
30949    /**
30950     * Read-only. The id of the container element becomes this TreePanel's id.
30951     */
30952     this.id = this.el.id;
30953     this.addEvents({
30954         /**
30955         * @event beforeload
30956         * Fires before a node is loaded, return false to cancel
30957         * @param {Node} node The node being loaded
30958         */
30959         "beforeload" : true,
30960         /**
30961         * @event load
30962         * Fires when a node is loaded
30963         * @param {Node} node The node that was loaded
30964         */
30965         "load" : true,
30966         /**
30967         * @event textchange
30968         * Fires when the text for a node is changed
30969         * @param {Node} node The node
30970         * @param {String} text The new text
30971         * @param {String} oldText The old text
30972         */
30973         "textchange" : true,
30974         /**
30975         * @event beforeexpand
30976         * Fires before a node is expanded, return false to cancel.
30977         * @param {Node} node The node
30978         * @param {Boolean} deep
30979         * @param {Boolean} anim
30980         */
30981         "beforeexpand" : true,
30982         /**
30983         * @event beforecollapse
30984         * Fires before a node is collapsed, return false to cancel.
30985         * @param {Node} node The node
30986         * @param {Boolean} deep
30987         * @param {Boolean} anim
30988         */
30989         "beforecollapse" : true,
30990         /**
30991         * @event expand
30992         * Fires when a node is expanded
30993         * @param {Node} node The node
30994         */
30995         "expand" : true,
30996         /**
30997         * @event disabledchange
30998         * Fires when the disabled status of a node changes
30999         * @param {Node} node The node
31000         * @param {Boolean} disabled
31001         */
31002         "disabledchange" : true,
31003         /**
31004         * @event collapse
31005         * Fires when a node is collapsed
31006         * @param {Node} node The node
31007         */
31008         "collapse" : true,
31009         /**
31010         * @event beforeclick
31011         * Fires before click processing on a node. Return false to cancel the default action.
31012         * @param {Node} node The node
31013         * @param {Roo.EventObject} e The event object
31014         */
31015         "beforeclick":true,
31016         /**
31017         * @event checkchange
31018         * Fires when a node with a checkbox's checked property changes
31019         * @param {Node} this This node
31020         * @param {Boolean} checked
31021         */
31022         "checkchange":true,
31023         /**
31024         * @event click
31025         * Fires when a node is clicked
31026         * @param {Node} node The node
31027         * @param {Roo.EventObject} e The event object
31028         */
31029         "click":true,
31030         /**
31031         * @event dblclick
31032         * Fires when a node is double clicked
31033         * @param {Node} node The node
31034         * @param {Roo.EventObject} e The event object
31035         */
31036         "dblclick":true,
31037         /**
31038         * @event contextmenu
31039         * Fires when a node is right clicked
31040         * @param {Node} node The node
31041         * @param {Roo.EventObject} e The event object
31042         */
31043         "contextmenu":true,
31044         /**
31045         * @event beforechildrenrendered
31046         * Fires right before the child nodes for a node are rendered
31047         * @param {Node} node The node
31048         */
31049         "beforechildrenrendered":true,
31050         /**
31051         * @event startdrag
31052         * Fires when a node starts being dragged
31053         * @param {Roo.tree.TreePanel} this
31054         * @param {Roo.tree.TreeNode} node
31055         * @param {event} e The raw browser event
31056         */ 
31057        "startdrag" : true,
31058        /**
31059         * @event enddrag
31060         * Fires when a drag operation is complete
31061         * @param {Roo.tree.TreePanel} this
31062         * @param {Roo.tree.TreeNode} node
31063         * @param {event} e The raw browser event
31064         */
31065        "enddrag" : true,
31066        /**
31067         * @event dragdrop
31068         * Fires when a dragged node is dropped on a valid DD target
31069         * @param {Roo.tree.TreePanel} this
31070         * @param {Roo.tree.TreeNode} node
31071         * @param {DD} dd The dd it was dropped on
31072         * @param {event} e The raw browser event
31073         */
31074        "dragdrop" : true,
31075        /**
31076         * @event beforenodedrop
31077         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31078         * passed to handlers has the following properties:<br />
31079         * <ul style="padding:5px;padding-left:16px;">
31080         * <li>tree - The TreePanel</li>
31081         * <li>target - The node being targeted for the drop</li>
31082         * <li>data - The drag data from the drag source</li>
31083         * <li>point - The point of the drop - append, above or below</li>
31084         * <li>source - The drag source</li>
31085         * <li>rawEvent - Raw mouse event</li>
31086         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31087         * to be inserted by setting them on this object.</li>
31088         * <li>cancel - Set this to true to cancel the drop.</li>
31089         * </ul>
31090         * @param {Object} dropEvent
31091         */
31092        "beforenodedrop" : true,
31093        /**
31094         * @event nodedrop
31095         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31096         * passed to handlers has the following properties:<br />
31097         * <ul style="padding:5px;padding-left:16px;">
31098         * <li>tree - The TreePanel</li>
31099         * <li>target - The node being targeted for the drop</li>
31100         * <li>data - The drag data from the drag source</li>
31101         * <li>point - The point of the drop - append, above or below</li>
31102         * <li>source - The drag source</li>
31103         * <li>rawEvent - Raw mouse event</li>
31104         * <li>dropNode - Dropped node(s).</li>
31105         * </ul>
31106         * @param {Object} dropEvent
31107         */
31108        "nodedrop" : true,
31109         /**
31110         * @event nodedragover
31111         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31112         * passed to handlers has the following properties:<br />
31113         * <ul style="padding:5px;padding-left:16px;">
31114         * <li>tree - The TreePanel</li>
31115         * <li>target - The node being targeted for the drop</li>
31116         * <li>data - The drag data from the drag source</li>
31117         * <li>point - The point of the drop - append, above or below</li>
31118         * <li>source - The drag source</li>
31119         * <li>rawEvent - Raw mouse event</li>
31120         * <li>dropNode - Drop node(s) provided by the source.</li>
31121         * <li>cancel - Set this to true to signal drop not allowed.</li>
31122         * </ul>
31123         * @param {Object} dragOverEvent
31124         */
31125        "nodedragover" : true
31126         
31127     });
31128     if(this.singleExpand){
31129        this.on("beforeexpand", this.restrictExpand, this);
31130     }
31131     if (this.editor) {
31132         this.editor.tree = this;
31133         this.editor = Roo.factory(this.editor, Roo.tree);
31134     }
31135     
31136     if (this.selModel) {
31137         this.selModel = Roo.factory(this.selModel, Roo.tree);
31138     }
31139    
31140 };
31141 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31142     rootVisible : true,
31143     animate: Roo.enableFx,
31144     lines : true,
31145     enableDD : false,
31146     hlDrop : Roo.enableFx,
31147   
31148     renderer: false,
31149     
31150     rendererTip: false,
31151     // private
31152     restrictExpand : function(node){
31153         var p = node.parentNode;
31154         if(p){
31155             if(p.expandedChild && p.expandedChild.parentNode == p){
31156                 p.expandedChild.collapse();
31157             }
31158             p.expandedChild = node;
31159         }
31160     },
31161
31162     // private override
31163     setRootNode : function(node){
31164         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31165         if(!this.rootVisible){
31166             node.ui = new Roo.tree.RootTreeNodeUI(node);
31167         }
31168         return node;
31169     },
31170
31171     /**
31172      * Returns the container element for this TreePanel
31173      */
31174     getEl : function(){
31175         return this.el;
31176     },
31177
31178     /**
31179      * Returns the default TreeLoader for this TreePanel
31180      */
31181     getLoader : function(){
31182         return this.loader;
31183     },
31184
31185     /**
31186      * Expand all nodes
31187      */
31188     expandAll : function(){
31189         this.root.expand(true);
31190     },
31191
31192     /**
31193      * Collapse all nodes
31194      */
31195     collapseAll : function(){
31196         this.root.collapse(true);
31197     },
31198
31199     /**
31200      * Returns the selection model used by this TreePanel
31201      */
31202     getSelectionModel : function(){
31203         if(!this.selModel){
31204             this.selModel = new Roo.tree.DefaultSelectionModel();
31205         }
31206         return this.selModel;
31207     },
31208
31209     /**
31210      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31211      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31212      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31213      * @return {Array}
31214      */
31215     getChecked : function(a, startNode){
31216         startNode = startNode || this.root;
31217         var r = [];
31218         var f = function(){
31219             if(this.attributes.checked){
31220                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31221             }
31222         }
31223         startNode.cascade(f);
31224         return r;
31225     },
31226
31227     /**
31228      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31229      * @param {String} path
31230      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31231      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31232      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31233      */
31234     expandPath : function(path, attr, callback){
31235         attr = attr || "id";
31236         var keys = path.split(this.pathSeparator);
31237         var curNode = this.root;
31238         if(curNode.attributes[attr] != keys[1]){ // invalid root
31239             if(callback){
31240                 callback(false, null);
31241             }
31242             return;
31243         }
31244         var index = 1;
31245         var f = function(){
31246             if(++index == keys.length){
31247                 if(callback){
31248                     callback(true, curNode);
31249                 }
31250                 return;
31251             }
31252             var c = curNode.findChild(attr, keys[index]);
31253             if(!c){
31254                 if(callback){
31255                     callback(false, curNode);
31256                 }
31257                 return;
31258             }
31259             curNode = c;
31260             c.expand(false, false, f);
31261         };
31262         curNode.expand(false, false, f);
31263     },
31264
31265     /**
31266      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31267      * @param {String} path
31268      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31269      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31270      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31271      */
31272     selectPath : function(path, attr, callback){
31273         attr = attr || "id";
31274         var keys = path.split(this.pathSeparator);
31275         var v = keys.pop();
31276         if(keys.length > 0){
31277             var f = function(success, node){
31278                 if(success && node){
31279                     var n = node.findChild(attr, v);
31280                     if(n){
31281                         n.select();
31282                         if(callback){
31283                             callback(true, n);
31284                         }
31285                     }else if(callback){
31286                         callback(false, n);
31287                     }
31288                 }else{
31289                     if(callback){
31290                         callback(false, n);
31291                     }
31292                 }
31293             };
31294             this.expandPath(keys.join(this.pathSeparator), attr, f);
31295         }else{
31296             this.root.select();
31297             if(callback){
31298                 callback(true, this.root);
31299             }
31300         }
31301     },
31302
31303     getTreeEl : function(){
31304         return this.el;
31305     },
31306
31307     /**
31308      * Trigger rendering of this TreePanel
31309      */
31310     render : function(){
31311         if (this.innerCt) {
31312             return this; // stop it rendering more than once!!
31313         }
31314         
31315         this.innerCt = this.el.createChild({tag:"ul",
31316                cls:"x-tree-root-ct " +
31317                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31318
31319         if(this.containerScroll){
31320             Roo.dd.ScrollManager.register(this.el);
31321         }
31322         if((this.enableDD || this.enableDrop) && !this.dropZone){
31323            /**
31324             * The dropZone used by this tree if drop is enabled
31325             * @type Roo.tree.TreeDropZone
31326             */
31327              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31328                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31329            });
31330         }
31331         if((this.enableDD || this.enableDrag) && !this.dragZone){
31332            /**
31333             * The dragZone used by this tree if drag is enabled
31334             * @type Roo.tree.TreeDragZone
31335             */
31336             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31337                ddGroup: this.ddGroup || "TreeDD",
31338                scroll: this.ddScroll
31339            });
31340         }
31341         this.getSelectionModel().init(this);
31342         if (!this.root) {
31343             console.log("ROOT not set in tree");
31344             return;
31345         }
31346         this.root.render();
31347         if(!this.rootVisible){
31348             this.root.renderChildren();
31349         }
31350         return this;
31351     }
31352 });/*
31353  * Based on:
31354  * Ext JS Library 1.1.1
31355  * Copyright(c) 2006-2007, Ext JS, LLC.
31356  *
31357  * Originally Released Under LGPL - original licence link has changed is not relivant.
31358  *
31359  * Fork - LGPL
31360  * <script type="text/javascript">
31361  */
31362  
31363
31364 /**
31365  * @class Roo.tree.DefaultSelectionModel
31366  * @extends Roo.util.Observable
31367  * The default single selection for a TreePanel.
31368  * @param {Object} cfg Configuration
31369  */
31370 Roo.tree.DefaultSelectionModel = function(cfg){
31371    this.selNode = null;
31372    
31373    
31374    
31375    this.addEvents({
31376        /**
31377         * @event selectionchange
31378         * Fires when the selected node changes
31379         * @param {DefaultSelectionModel} this
31380         * @param {TreeNode} node the new selection
31381         */
31382        "selectionchange" : true,
31383
31384        /**
31385         * @event beforeselect
31386         * Fires before the selected node changes, return false to cancel the change
31387         * @param {DefaultSelectionModel} this
31388         * @param {TreeNode} node the new selection
31389         * @param {TreeNode} node the old selection
31390         */
31391        "beforeselect" : true
31392    });
31393    
31394     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31395 };
31396
31397 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31398     init : function(tree){
31399         this.tree = tree;
31400         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31401         tree.on("click", this.onNodeClick, this);
31402     },
31403     
31404     onNodeClick : function(node, e){
31405         if (e.ctrlKey && this.selNode == node)  {
31406             this.unselect(node);
31407             return;
31408         }
31409         this.select(node);
31410     },
31411     
31412     /**
31413      * Select a node.
31414      * @param {TreeNode} node The node to select
31415      * @return {TreeNode} The selected node
31416      */
31417     select : function(node){
31418         var last = this.selNode;
31419         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31420             if(last){
31421                 last.ui.onSelectedChange(false);
31422             }
31423             this.selNode = node;
31424             node.ui.onSelectedChange(true);
31425             this.fireEvent("selectionchange", this, node, last);
31426         }
31427         return node;
31428     },
31429     
31430     /**
31431      * Deselect a node.
31432      * @param {TreeNode} node The node to unselect
31433      */
31434     unselect : function(node){
31435         if(this.selNode == node){
31436             this.clearSelections();
31437         }    
31438     },
31439     
31440     /**
31441      * Clear all selections
31442      */
31443     clearSelections : function(){
31444         var n = this.selNode;
31445         if(n){
31446             n.ui.onSelectedChange(false);
31447             this.selNode = null;
31448             this.fireEvent("selectionchange", this, null);
31449         }
31450         return n;
31451     },
31452     
31453     /**
31454      * Get the selected node
31455      * @return {TreeNode} The selected node
31456      */
31457     getSelectedNode : function(){
31458         return this.selNode;    
31459     },
31460     
31461     /**
31462      * Returns true if the node is selected
31463      * @param {TreeNode} node The node to check
31464      * @return {Boolean}
31465      */
31466     isSelected : function(node){
31467         return this.selNode == node;  
31468     },
31469
31470     /**
31471      * Selects the node above the selected node in the tree, intelligently walking the nodes
31472      * @return TreeNode The new selection
31473      */
31474     selectPrevious : function(){
31475         var s = this.selNode || this.lastSelNode;
31476         if(!s){
31477             return null;
31478         }
31479         var ps = s.previousSibling;
31480         if(ps){
31481             if(!ps.isExpanded() || ps.childNodes.length < 1){
31482                 return this.select(ps);
31483             } else{
31484                 var lc = ps.lastChild;
31485                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31486                     lc = lc.lastChild;
31487                 }
31488                 return this.select(lc);
31489             }
31490         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31491             return this.select(s.parentNode);
31492         }
31493         return null;
31494     },
31495
31496     /**
31497      * Selects the node above the selected node in the tree, intelligently walking the nodes
31498      * @return TreeNode The new selection
31499      */
31500     selectNext : function(){
31501         var s = this.selNode || this.lastSelNode;
31502         if(!s){
31503             return null;
31504         }
31505         if(s.firstChild && s.isExpanded()){
31506              return this.select(s.firstChild);
31507          }else if(s.nextSibling){
31508              return this.select(s.nextSibling);
31509          }else if(s.parentNode){
31510             var newS = null;
31511             s.parentNode.bubble(function(){
31512                 if(this.nextSibling){
31513                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31514                     return false;
31515                 }
31516             });
31517             return newS;
31518          }
31519         return null;
31520     },
31521
31522     onKeyDown : function(e){
31523         var s = this.selNode || this.lastSelNode;
31524         // undesirable, but required
31525         var sm = this;
31526         if(!s){
31527             return;
31528         }
31529         var k = e.getKey();
31530         switch(k){
31531              case e.DOWN:
31532                  e.stopEvent();
31533                  this.selectNext();
31534              break;
31535              case e.UP:
31536                  e.stopEvent();
31537                  this.selectPrevious();
31538              break;
31539              case e.RIGHT:
31540                  e.preventDefault();
31541                  if(s.hasChildNodes()){
31542                      if(!s.isExpanded()){
31543                          s.expand();
31544                      }else if(s.firstChild){
31545                          this.select(s.firstChild, e);
31546                      }
31547                  }
31548              break;
31549              case e.LEFT:
31550                  e.preventDefault();
31551                  if(s.hasChildNodes() && s.isExpanded()){
31552                      s.collapse();
31553                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31554                      this.select(s.parentNode, e);
31555                  }
31556              break;
31557         };
31558     }
31559 });
31560
31561 /**
31562  * @class Roo.tree.MultiSelectionModel
31563  * @extends Roo.util.Observable
31564  * Multi selection for a TreePanel.
31565  * @param {Object} cfg Configuration
31566  */
31567 Roo.tree.MultiSelectionModel = function(){
31568    this.selNodes = [];
31569    this.selMap = {};
31570    this.addEvents({
31571        /**
31572         * @event selectionchange
31573         * Fires when the selected nodes change
31574         * @param {MultiSelectionModel} this
31575         * @param {Array} nodes Array of the selected nodes
31576         */
31577        "selectionchange" : true
31578    });
31579    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31580    
31581 };
31582
31583 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31584     init : function(tree){
31585         this.tree = tree;
31586         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31587         tree.on("click", this.onNodeClick, this);
31588     },
31589     
31590     onNodeClick : function(node, e){
31591         this.select(node, e, e.ctrlKey);
31592     },
31593     
31594     /**
31595      * Select a node.
31596      * @param {TreeNode} node The node to select
31597      * @param {EventObject} e (optional) An event associated with the selection
31598      * @param {Boolean} keepExisting True to retain existing selections
31599      * @return {TreeNode} The selected node
31600      */
31601     select : function(node, e, keepExisting){
31602         if(keepExisting !== true){
31603             this.clearSelections(true);
31604         }
31605         if(this.isSelected(node)){
31606             this.lastSelNode = node;
31607             return node;
31608         }
31609         this.selNodes.push(node);
31610         this.selMap[node.id] = node;
31611         this.lastSelNode = node;
31612         node.ui.onSelectedChange(true);
31613         this.fireEvent("selectionchange", this, this.selNodes);
31614         return node;
31615     },
31616     
31617     /**
31618      * Deselect a node.
31619      * @param {TreeNode} node The node to unselect
31620      */
31621     unselect : function(node){
31622         if(this.selMap[node.id]){
31623             node.ui.onSelectedChange(false);
31624             var sn = this.selNodes;
31625             var index = -1;
31626             if(sn.indexOf){
31627                 index = sn.indexOf(node);
31628             }else{
31629                 for(var i = 0, len = sn.length; i < len; i++){
31630                     if(sn[i] == node){
31631                         index = i;
31632                         break;
31633                     }
31634                 }
31635             }
31636             if(index != -1){
31637                 this.selNodes.splice(index, 1);
31638             }
31639             delete this.selMap[node.id];
31640             this.fireEvent("selectionchange", this, this.selNodes);
31641         }
31642     },
31643     
31644     /**
31645      * Clear all selections
31646      */
31647     clearSelections : function(suppressEvent){
31648         var sn = this.selNodes;
31649         if(sn.length > 0){
31650             for(var i = 0, len = sn.length; i < len; i++){
31651                 sn[i].ui.onSelectedChange(false);
31652             }
31653             this.selNodes = [];
31654             this.selMap = {};
31655             if(suppressEvent !== true){
31656                 this.fireEvent("selectionchange", this, this.selNodes);
31657             }
31658         }
31659     },
31660     
31661     /**
31662      * Returns true if the node is selected
31663      * @param {TreeNode} node The node to check
31664      * @return {Boolean}
31665      */
31666     isSelected : function(node){
31667         return this.selMap[node.id] ? true : false;  
31668     },
31669     
31670     /**
31671      * Returns an array of the selected nodes
31672      * @return {Array}
31673      */
31674     getSelectedNodes : function(){
31675         return this.selNodes;    
31676     },
31677
31678     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31679
31680     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31681
31682     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31683 });/*
31684  * Based on:
31685  * Ext JS Library 1.1.1
31686  * Copyright(c) 2006-2007, Ext JS, LLC.
31687  *
31688  * Originally Released Under LGPL - original licence link has changed is not relivant.
31689  *
31690  * Fork - LGPL
31691  * <script type="text/javascript">
31692  */
31693  
31694 /**
31695  * @class Roo.tree.TreeNode
31696  * @extends Roo.data.Node
31697  * @cfg {String} text The text for this node
31698  * @cfg {Boolean} expanded true to start the node expanded
31699  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31700  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31701  * @cfg {Boolean} disabled true to start the node disabled
31702  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31703  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31704  * @cfg {String} cls A css class to be added to the node
31705  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31706  * @cfg {String} href URL of the link used for the node (defaults to #)
31707  * @cfg {String} hrefTarget target frame for the link
31708  * @cfg {String} qtip An Ext QuickTip for the node
31709  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31710  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31711  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31712  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31713  * (defaults to undefined with no checkbox rendered)
31714  * @constructor
31715  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31716  */
31717 Roo.tree.TreeNode = function(attributes){
31718     attributes = attributes || {};
31719     if(typeof attributes == "string"){
31720         attributes = {text: attributes};
31721     }
31722     this.childrenRendered = false;
31723     this.rendered = false;
31724     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31725     this.expanded = attributes.expanded === true;
31726     this.isTarget = attributes.isTarget !== false;
31727     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31728     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31729
31730     /**
31731      * Read-only. The text for this node. To change it use setText().
31732      * @type String
31733      */
31734     this.text = attributes.text;
31735     /**
31736      * True if this node is disabled.
31737      * @type Boolean
31738      */
31739     this.disabled = attributes.disabled === true;
31740
31741     this.addEvents({
31742         /**
31743         * @event textchange
31744         * Fires when the text for this node is changed
31745         * @param {Node} this This node
31746         * @param {String} text The new text
31747         * @param {String} oldText The old text
31748         */
31749         "textchange" : true,
31750         /**
31751         * @event beforeexpand
31752         * Fires before this node is expanded, return false to cancel.
31753         * @param {Node} this This node
31754         * @param {Boolean} deep
31755         * @param {Boolean} anim
31756         */
31757         "beforeexpand" : true,
31758         /**
31759         * @event beforecollapse
31760         * Fires before this node is collapsed, return false to cancel.
31761         * @param {Node} this This node
31762         * @param {Boolean} deep
31763         * @param {Boolean} anim
31764         */
31765         "beforecollapse" : true,
31766         /**
31767         * @event expand
31768         * Fires when this node is expanded
31769         * @param {Node} this This node
31770         */
31771         "expand" : true,
31772         /**
31773         * @event disabledchange
31774         * Fires when the disabled status of this node changes
31775         * @param {Node} this This node
31776         * @param {Boolean} disabled
31777         */
31778         "disabledchange" : true,
31779         /**
31780         * @event collapse
31781         * Fires when this node is collapsed
31782         * @param {Node} this This node
31783         */
31784         "collapse" : true,
31785         /**
31786         * @event beforeclick
31787         * Fires before click processing. Return false to cancel the default action.
31788         * @param {Node} this This node
31789         * @param {Roo.EventObject} e The event object
31790         */
31791         "beforeclick":true,
31792         /**
31793         * @event checkchange
31794         * Fires when a node with a checkbox's checked property changes
31795         * @param {Node} this This node
31796         * @param {Boolean} checked
31797         */
31798         "checkchange":true,
31799         /**
31800         * @event click
31801         * Fires when this node is clicked
31802         * @param {Node} this This node
31803         * @param {Roo.EventObject} e The event object
31804         */
31805         "click":true,
31806         /**
31807         * @event dblclick
31808         * Fires when this node is double clicked
31809         * @param {Node} this This node
31810         * @param {Roo.EventObject} e The event object
31811         */
31812         "dblclick":true,
31813         /**
31814         * @event contextmenu
31815         * Fires when this node is right clicked
31816         * @param {Node} this This node
31817         * @param {Roo.EventObject} e The event object
31818         */
31819         "contextmenu":true,
31820         /**
31821         * @event beforechildrenrendered
31822         * Fires right before the child nodes for this node are rendered
31823         * @param {Node} this This node
31824         */
31825         "beforechildrenrendered":true
31826     });
31827
31828     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31829
31830     /**
31831      * Read-only. The UI for this node
31832      * @type TreeNodeUI
31833      */
31834     this.ui = new uiClass(this);
31835 };
31836 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31837     preventHScroll: true,
31838     /**
31839      * Returns true if this node is expanded
31840      * @return {Boolean}
31841      */
31842     isExpanded : function(){
31843         return this.expanded;
31844     },
31845
31846     /**
31847      * Returns the UI object for this node
31848      * @return {TreeNodeUI}
31849      */
31850     getUI : function(){
31851         return this.ui;
31852     },
31853
31854     // private override
31855     setFirstChild : function(node){
31856         var of = this.firstChild;
31857         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31858         if(this.childrenRendered && of && node != of){
31859             of.renderIndent(true, true);
31860         }
31861         if(this.rendered){
31862             this.renderIndent(true, true);
31863         }
31864     },
31865
31866     // private override
31867     setLastChild : function(node){
31868         var ol = this.lastChild;
31869         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31870         if(this.childrenRendered && ol && node != ol){
31871             ol.renderIndent(true, true);
31872         }
31873         if(this.rendered){
31874             this.renderIndent(true, true);
31875         }
31876     },
31877
31878     // these methods are overridden to provide lazy rendering support
31879     // private override
31880     appendChild : function(){
31881         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31882         if(node && this.childrenRendered){
31883             node.render();
31884         }
31885         this.ui.updateExpandIcon();
31886         return node;
31887     },
31888
31889     // private override
31890     removeChild : function(node){
31891         this.ownerTree.getSelectionModel().unselect(node);
31892         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31893         // if it's been rendered remove dom node
31894         if(this.childrenRendered){
31895             node.ui.remove();
31896         }
31897         if(this.childNodes.length < 1){
31898             this.collapse(false, false);
31899         }else{
31900             this.ui.updateExpandIcon();
31901         }
31902         if(!this.firstChild) {
31903             this.childrenRendered = false;
31904         }
31905         return node;
31906     },
31907
31908     // private override
31909     insertBefore : function(node, refNode){
31910         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31911         if(newNode && refNode && this.childrenRendered){
31912             node.render();
31913         }
31914         this.ui.updateExpandIcon();
31915         return newNode;
31916     },
31917
31918     /**
31919      * Sets the text for this node
31920      * @param {String} text
31921      */
31922     setText : function(text){
31923         var oldText = this.text;
31924         this.text = text;
31925         this.attributes.text = text;
31926         if(this.rendered){ // event without subscribing
31927             this.ui.onTextChange(this, text, oldText);
31928         }
31929         this.fireEvent("textchange", this, text, oldText);
31930     },
31931
31932     /**
31933      * Triggers selection of this node
31934      */
31935     select : function(){
31936         this.getOwnerTree().getSelectionModel().select(this);
31937     },
31938
31939     /**
31940      * Triggers deselection of this node
31941      */
31942     unselect : function(){
31943         this.getOwnerTree().getSelectionModel().unselect(this);
31944     },
31945
31946     /**
31947      * Returns true if this node is selected
31948      * @return {Boolean}
31949      */
31950     isSelected : function(){
31951         return this.getOwnerTree().getSelectionModel().isSelected(this);
31952     },
31953
31954     /**
31955      * Expand this node.
31956      * @param {Boolean} deep (optional) True to expand all children as well
31957      * @param {Boolean} anim (optional) false to cancel the default animation
31958      * @param {Function} callback (optional) A callback to be called when
31959      * expanding this node completes (does not wait for deep expand to complete).
31960      * Called with 1 parameter, this node.
31961      */
31962     expand : function(deep, anim, callback){
31963         if(!this.expanded){
31964             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31965                 return;
31966             }
31967             if(!this.childrenRendered){
31968                 this.renderChildren();
31969             }
31970             this.expanded = true;
31971             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31972                 this.ui.animExpand(function(){
31973                     this.fireEvent("expand", this);
31974                     if(typeof callback == "function"){
31975                         callback(this);
31976                     }
31977                     if(deep === true){
31978                         this.expandChildNodes(true);
31979                     }
31980                 }.createDelegate(this));
31981                 return;
31982             }else{
31983                 this.ui.expand();
31984                 this.fireEvent("expand", this);
31985                 if(typeof callback == "function"){
31986                     callback(this);
31987                 }
31988             }
31989         }else{
31990            if(typeof callback == "function"){
31991                callback(this);
31992            }
31993         }
31994         if(deep === true){
31995             this.expandChildNodes(true);
31996         }
31997     },
31998
31999     isHiddenRoot : function(){
32000         return this.isRoot && !this.getOwnerTree().rootVisible;
32001     },
32002
32003     /**
32004      * Collapse this node.
32005      * @param {Boolean} deep (optional) True to collapse all children as well
32006      * @param {Boolean} anim (optional) false to cancel the default animation
32007      */
32008     collapse : function(deep, anim){
32009         if(this.expanded && !this.isHiddenRoot()){
32010             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32011                 return;
32012             }
32013             this.expanded = false;
32014             if((this.getOwnerTree().animate && anim !== false) || anim){
32015                 this.ui.animCollapse(function(){
32016                     this.fireEvent("collapse", this);
32017                     if(deep === true){
32018                         this.collapseChildNodes(true);
32019                     }
32020                 }.createDelegate(this));
32021                 return;
32022             }else{
32023                 this.ui.collapse();
32024                 this.fireEvent("collapse", this);
32025             }
32026         }
32027         if(deep === true){
32028             var cs = this.childNodes;
32029             for(var i = 0, len = cs.length; i < len; i++) {
32030                 cs[i].collapse(true, false);
32031             }
32032         }
32033     },
32034
32035     // private
32036     delayedExpand : function(delay){
32037         if(!this.expandProcId){
32038             this.expandProcId = this.expand.defer(delay, this);
32039         }
32040     },
32041
32042     // private
32043     cancelExpand : function(){
32044         if(this.expandProcId){
32045             clearTimeout(this.expandProcId);
32046         }
32047         this.expandProcId = false;
32048     },
32049
32050     /**
32051      * Toggles expanded/collapsed state of the node
32052      */
32053     toggle : function(){
32054         if(this.expanded){
32055             this.collapse();
32056         }else{
32057             this.expand();
32058         }
32059     },
32060
32061     /**
32062      * Ensures all parent nodes are expanded
32063      */
32064     ensureVisible : function(callback){
32065         var tree = this.getOwnerTree();
32066         tree.expandPath(this.parentNode.getPath(), false, function(){
32067             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32068             Roo.callback(callback);
32069         }.createDelegate(this));
32070     },
32071
32072     /**
32073      * Expand all child nodes
32074      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32075      */
32076     expandChildNodes : function(deep){
32077         var cs = this.childNodes;
32078         for(var i = 0, len = cs.length; i < len; i++) {
32079                 cs[i].expand(deep);
32080         }
32081     },
32082
32083     /**
32084      * Collapse all child nodes
32085      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32086      */
32087     collapseChildNodes : function(deep){
32088         var cs = this.childNodes;
32089         for(var i = 0, len = cs.length; i < len; i++) {
32090                 cs[i].collapse(deep);
32091         }
32092     },
32093
32094     /**
32095      * Disables this node
32096      */
32097     disable : function(){
32098         this.disabled = true;
32099         this.unselect();
32100         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32101             this.ui.onDisableChange(this, true);
32102         }
32103         this.fireEvent("disabledchange", this, true);
32104     },
32105
32106     /**
32107      * Enables this node
32108      */
32109     enable : function(){
32110         this.disabled = false;
32111         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32112             this.ui.onDisableChange(this, false);
32113         }
32114         this.fireEvent("disabledchange", this, false);
32115     },
32116
32117     // private
32118     renderChildren : function(suppressEvent){
32119         if(suppressEvent !== false){
32120             this.fireEvent("beforechildrenrendered", this);
32121         }
32122         var cs = this.childNodes;
32123         for(var i = 0, len = cs.length; i < len; i++){
32124             cs[i].render(true);
32125         }
32126         this.childrenRendered = true;
32127     },
32128
32129     // private
32130     sort : function(fn, scope){
32131         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32132         if(this.childrenRendered){
32133             var cs = this.childNodes;
32134             for(var i = 0, len = cs.length; i < len; i++){
32135                 cs[i].render(true);
32136             }
32137         }
32138     },
32139
32140     // private
32141     render : function(bulkRender){
32142         this.ui.render(bulkRender);
32143         if(!this.rendered){
32144             this.rendered = true;
32145             if(this.expanded){
32146                 this.expanded = false;
32147                 this.expand(false, false);
32148             }
32149         }
32150     },
32151
32152     // private
32153     renderIndent : function(deep, refresh){
32154         if(refresh){
32155             this.ui.childIndent = null;
32156         }
32157         this.ui.renderIndent();
32158         if(deep === true && this.childrenRendered){
32159             var cs = this.childNodes;
32160             for(var i = 0, len = cs.length; i < len; i++){
32161                 cs[i].renderIndent(true, refresh);
32162             }
32163         }
32164     }
32165 });/*
32166  * Based on:
32167  * Ext JS Library 1.1.1
32168  * Copyright(c) 2006-2007, Ext JS, LLC.
32169  *
32170  * Originally Released Under LGPL - original licence link has changed is not relivant.
32171  *
32172  * Fork - LGPL
32173  * <script type="text/javascript">
32174  */
32175  
32176 /**
32177  * @class Roo.tree.AsyncTreeNode
32178  * @extends Roo.tree.TreeNode
32179  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32180  * @constructor
32181  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32182  */
32183  Roo.tree.AsyncTreeNode = function(config){
32184     this.loaded = false;
32185     this.loading = false;
32186     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32187     /**
32188     * @event beforeload
32189     * Fires before this node is loaded, return false to cancel
32190     * @param {Node} this This node
32191     */
32192     this.addEvents({'beforeload':true, 'load': true});
32193     /**
32194     * @event load
32195     * Fires when this node is loaded
32196     * @param {Node} this This node
32197     */
32198     /**
32199      * The loader used by this node (defaults to using the tree's defined loader)
32200      * @type TreeLoader
32201      * @property loader
32202      */
32203 };
32204 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32205     expand : function(deep, anim, callback){
32206         if(this.loading){ // if an async load is already running, waiting til it's done
32207             var timer;
32208             var f = function(){
32209                 if(!this.loading){ // done loading
32210                     clearInterval(timer);
32211                     this.expand(deep, anim, callback);
32212                 }
32213             }.createDelegate(this);
32214             timer = setInterval(f, 200);
32215             return;
32216         }
32217         if(!this.loaded){
32218             if(this.fireEvent("beforeload", this) === false){
32219                 return;
32220             }
32221             this.loading = true;
32222             this.ui.beforeLoad(this);
32223             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32224             if(loader){
32225                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32226                 return;
32227             }
32228         }
32229         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32230     },
32231     
32232     /**
32233      * Returns true if this node is currently loading
32234      * @return {Boolean}
32235      */
32236     isLoading : function(){
32237         return this.loading;  
32238     },
32239     
32240     loadComplete : function(deep, anim, callback){
32241         this.loading = false;
32242         this.loaded = true;
32243         this.ui.afterLoad(this);
32244         this.fireEvent("load", this);
32245         this.expand(deep, anim, callback);
32246     },
32247     
32248     /**
32249      * Returns true if this node has been loaded
32250      * @return {Boolean}
32251      */
32252     isLoaded : function(){
32253         return this.loaded;
32254     },
32255     
32256     hasChildNodes : function(){
32257         if(!this.isLeaf() && !this.loaded){
32258             return true;
32259         }else{
32260             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32261         }
32262     },
32263
32264     /**
32265      * Trigger a reload for this node
32266      * @param {Function} callback
32267      */
32268     reload : function(callback){
32269         this.collapse(false, false);
32270         while(this.firstChild){
32271             this.removeChild(this.firstChild);
32272         }
32273         this.childrenRendered = false;
32274         this.loaded = false;
32275         if(this.isHiddenRoot()){
32276             this.expanded = false;
32277         }
32278         this.expand(false, false, callback);
32279     }
32280 });/*
32281  * Based on:
32282  * Ext JS Library 1.1.1
32283  * Copyright(c) 2006-2007, Ext JS, LLC.
32284  *
32285  * Originally Released Under LGPL - original licence link has changed is not relivant.
32286  *
32287  * Fork - LGPL
32288  * <script type="text/javascript">
32289  */
32290  
32291 /**
32292  * @class Roo.tree.TreeNodeUI
32293  * @constructor
32294  * @param {Object} node The node to render
32295  * The TreeNode UI implementation is separate from the
32296  * tree implementation. Unless you are customizing the tree UI,
32297  * you should never have to use this directly.
32298  */
32299 Roo.tree.TreeNodeUI = function(node){
32300     this.node = node;
32301     this.rendered = false;
32302     this.animating = false;
32303     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32304 };
32305
32306 Roo.tree.TreeNodeUI.prototype = {
32307     removeChild : function(node){
32308         if(this.rendered){
32309             this.ctNode.removeChild(node.ui.getEl());
32310         }
32311     },
32312
32313     beforeLoad : function(){
32314          this.addClass("x-tree-node-loading");
32315     },
32316
32317     afterLoad : function(){
32318          this.removeClass("x-tree-node-loading");
32319     },
32320
32321     onTextChange : function(node, text, oldText){
32322         if(this.rendered){
32323             this.textNode.innerHTML = text;
32324         }
32325     },
32326
32327     onDisableChange : function(node, state){
32328         this.disabled = state;
32329         if(state){
32330             this.addClass("x-tree-node-disabled");
32331         }else{
32332             this.removeClass("x-tree-node-disabled");
32333         }
32334     },
32335
32336     onSelectedChange : function(state){
32337         if(state){
32338             this.focus();
32339             this.addClass("x-tree-selected");
32340         }else{
32341             //this.blur();
32342             this.removeClass("x-tree-selected");
32343         }
32344     },
32345
32346     onMove : function(tree, node, oldParent, newParent, index, refNode){
32347         this.childIndent = null;
32348         if(this.rendered){
32349             var targetNode = newParent.ui.getContainer();
32350             if(!targetNode){//target not rendered
32351                 this.holder = document.createElement("div");
32352                 this.holder.appendChild(this.wrap);
32353                 return;
32354             }
32355             var insertBefore = refNode ? refNode.ui.getEl() : null;
32356             if(insertBefore){
32357                 targetNode.insertBefore(this.wrap, insertBefore);
32358             }else{
32359                 targetNode.appendChild(this.wrap);
32360             }
32361             this.node.renderIndent(true);
32362         }
32363     },
32364
32365     addClass : function(cls){
32366         if(this.elNode){
32367             Roo.fly(this.elNode).addClass(cls);
32368         }
32369     },
32370
32371     removeClass : function(cls){
32372         if(this.elNode){
32373             Roo.fly(this.elNode).removeClass(cls);
32374         }
32375     },
32376
32377     remove : function(){
32378         if(this.rendered){
32379             this.holder = document.createElement("div");
32380             this.holder.appendChild(this.wrap);
32381         }
32382     },
32383
32384     fireEvent : function(){
32385         return this.node.fireEvent.apply(this.node, arguments);
32386     },
32387
32388     initEvents : function(){
32389         this.node.on("move", this.onMove, this);
32390         var E = Roo.EventManager;
32391         var a = this.anchor;
32392
32393         var el = Roo.fly(a, '_treeui');
32394
32395         if(Roo.isOpera){ // opera render bug ignores the CSS
32396             el.setStyle("text-decoration", "none");
32397         }
32398
32399         el.on("click", this.onClick, this);
32400         el.on("dblclick", this.onDblClick, this);
32401
32402         if(this.checkbox){
32403             Roo.EventManager.on(this.checkbox,
32404                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32405         }
32406
32407         el.on("contextmenu", this.onContextMenu, this);
32408
32409         var icon = Roo.fly(this.iconNode);
32410         icon.on("click", this.onClick, this);
32411         icon.on("dblclick", this.onDblClick, this);
32412         icon.on("contextmenu", this.onContextMenu, this);
32413         E.on(this.ecNode, "click", this.ecClick, this, true);
32414
32415         if(this.node.disabled){
32416             this.addClass("x-tree-node-disabled");
32417         }
32418         if(this.node.hidden){
32419             this.addClass("x-tree-node-disabled");
32420         }
32421         var ot = this.node.getOwnerTree();
32422         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32423         if(dd && (!this.node.isRoot || ot.rootVisible)){
32424             Roo.dd.Registry.register(this.elNode, {
32425                 node: this.node,
32426                 handles: this.getDDHandles(),
32427                 isHandle: false
32428             });
32429         }
32430     },
32431
32432     getDDHandles : function(){
32433         return [this.iconNode, this.textNode];
32434     },
32435
32436     hide : function(){
32437         if(this.rendered){
32438             this.wrap.style.display = "none";
32439         }
32440     },
32441
32442     show : function(){
32443         if(this.rendered){
32444             this.wrap.style.display = "";
32445         }
32446     },
32447
32448     onContextMenu : function(e){
32449         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32450             e.preventDefault();
32451             this.focus();
32452             this.fireEvent("contextmenu", this.node, e);
32453         }
32454     },
32455
32456     onClick : function(e){
32457         if(this.dropping){
32458             e.stopEvent();
32459             return;
32460         }
32461         if(this.fireEvent("beforeclick", this.node, e) !== false){
32462             if(!this.disabled && this.node.attributes.href){
32463                 this.fireEvent("click", this.node, e);
32464                 return;
32465             }
32466             e.preventDefault();
32467             if(this.disabled){
32468                 return;
32469             }
32470
32471             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32472                 this.node.toggle();
32473             }
32474
32475             this.fireEvent("click", this.node, e);
32476         }else{
32477             e.stopEvent();
32478         }
32479     },
32480
32481     onDblClick : function(e){
32482         e.preventDefault();
32483         if(this.disabled){
32484             return;
32485         }
32486         if(this.checkbox){
32487             this.toggleCheck();
32488         }
32489         if(!this.animating && this.node.hasChildNodes()){
32490             this.node.toggle();
32491         }
32492         this.fireEvent("dblclick", this.node, e);
32493     },
32494
32495     onCheckChange : function(){
32496         var checked = this.checkbox.checked;
32497         this.node.attributes.checked = checked;
32498         this.fireEvent('checkchange', this.node, checked);
32499     },
32500
32501     ecClick : function(e){
32502         if(!this.animating && this.node.hasChildNodes()){
32503             this.node.toggle();
32504         }
32505     },
32506
32507     startDrop : function(){
32508         this.dropping = true;
32509     },
32510
32511     // delayed drop so the click event doesn't get fired on a drop
32512     endDrop : function(){
32513        setTimeout(function(){
32514            this.dropping = false;
32515        }.createDelegate(this), 50);
32516     },
32517
32518     expand : function(){
32519         this.updateExpandIcon();
32520         this.ctNode.style.display = "";
32521     },
32522
32523     focus : function(){
32524         if(!this.node.preventHScroll){
32525             try{this.anchor.focus();
32526             }catch(e){}
32527         }else if(!Roo.isIE){
32528             try{
32529                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32530                 var l = noscroll.scrollLeft;
32531                 this.anchor.focus();
32532                 noscroll.scrollLeft = l;
32533             }catch(e){}
32534         }
32535     },
32536
32537     toggleCheck : function(value){
32538         var cb = this.checkbox;
32539         if(cb){
32540             cb.checked = (value === undefined ? !cb.checked : value);
32541         }
32542     },
32543
32544     blur : function(){
32545         try{
32546             this.anchor.blur();
32547         }catch(e){}
32548     },
32549
32550     animExpand : function(callback){
32551         var ct = Roo.get(this.ctNode);
32552         ct.stopFx();
32553         if(!this.node.hasChildNodes()){
32554             this.updateExpandIcon();
32555             this.ctNode.style.display = "";
32556             Roo.callback(callback);
32557             return;
32558         }
32559         this.animating = true;
32560         this.updateExpandIcon();
32561
32562         ct.slideIn('t', {
32563            callback : function(){
32564                this.animating = false;
32565                Roo.callback(callback);
32566             },
32567             scope: this,
32568             duration: this.node.ownerTree.duration || .25
32569         });
32570     },
32571
32572     highlight : function(){
32573         var tree = this.node.getOwnerTree();
32574         Roo.fly(this.wrap).highlight(
32575             tree.hlColor || "C3DAF9",
32576             {endColor: tree.hlBaseColor}
32577         );
32578     },
32579
32580     collapse : function(){
32581         this.updateExpandIcon();
32582         this.ctNode.style.display = "none";
32583     },
32584
32585     animCollapse : function(callback){
32586         var ct = Roo.get(this.ctNode);
32587         ct.enableDisplayMode('block');
32588         ct.stopFx();
32589
32590         this.animating = true;
32591         this.updateExpandIcon();
32592
32593         ct.slideOut('t', {
32594             callback : function(){
32595                this.animating = false;
32596                Roo.callback(callback);
32597             },
32598             scope: this,
32599             duration: this.node.ownerTree.duration || .25
32600         });
32601     },
32602
32603     getContainer : function(){
32604         return this.ctNode;
32605     },
32606
32607     getEl : function(){
32608         return this.wrap;
32609     },
32610
32611     appendDDGhost : function(ghostNode){
32612         ghostNode.appendChild(this.elNode.cloneNode(true));
32613     },
32614
32615     getDDRepairXY : function(){
32616         return Roo.lib.Dom.getXY(this.iconNode);
32617     },
32618
32619     onRender : function(){
32620         this.render();
32621     },
32622
32623     render : function(bulkRender){
32624         var n = this.node, a = n.attributes;
32625         var targetNode = n.parentNode ?
32626               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32627
32628         if(!this.rendered){
32629             this.rendered = true;
32630
32631             this.renderElements(n, a, targetNode, bulkRender);
32632
32633             if(a.qtip){
32634                if(this.textNode.setAttributeNS){
32635                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32636                    if(a.qtipTitle){
32637                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32638                    }
32639                }else{
32640                    this.textNode.setAttribute("ext:qtip", a.qtip);
32641                    if(a.qtipTitle){
32642                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32643                    }
32644                }
32645             }else if(a.qtipCfg){
32646                 a.qtipCfg.target = Roo.id(this.textNode);
32647                 Roo.QuickTips.register(a.qtipCfg);
32648             }
32649             this.initEvents();
32650             if(!this.node.expanded){
32651                 this.updateExpandIcon();
32652             }
32653         }else{
32654             if(bulkRender === true) {
32655                 targetNode.appendChild(this.wrap);
32656             }
32657         }
32658     },
32659
32660     renderElements : function(n, a, targetNode, bulkRender)
32661     {
32662         // add some indent caching, this helps performance when rendering a large tree
32663         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32664         var t = n.getOwnerTree();
32665         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32666         if (typeof(n.attributes.html) != 'undefined') {
32667             txt = n.attributes.html;
32668         }
32669         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32670         var cb = typeof a.checked == 'boolean';
32671         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32672         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32673             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32674             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32675             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32676             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32677             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32678              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32679                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32680             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32681             "</li>"];
32682
32683         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32684             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32685                                 n.nextSibling.ui.getEl(), buf.join(""));
32686         }else{
32687             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32688         }
32689
32690         this.elNode = this.wrap.childNodes[0];
32691         this.ctNode = this.wrap.childNodes[1];
32692         var cs = this.elNode.childNodes;
32693         this.indentNode = cs[0];
32694         this.ecNode = cs[1];
32695         this.iconNode = cs[2];
32696         var index = 3;
32697         if(cb){
32698             this.checkbox = cs[3];
32699             index++;
32700         }
32701         this.anchor = cs[index];
32702         this.textNode = cs[index].firstChild;
32703     },
32704
32705     getAnchor : function(){
32706         return this.anchor;
32707     },
32708
32709     getTextEl : function(){
32710         return this.textNode;
32711     },
32712
32713     getIconEl : function(){
32714         return this.iconNode;
32715     },
32716
32717     isChecked : function(){
32718         return this.checkbox ? this.checkbox.checked : false;
32719     },
32720
32721     updateExpandIcon : function(){
32722         if(this.rendered){
32723             var n = this.node, c1, c2;
32724             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32725             var hasChild = n.hasChildNodes();
32726             if(hasChild){
32727                 if(n.expanded){
32728                     cls += "-minus";
32729                     c1 = "x-tree-node-collapsed";
32730                     c2 = "x-tree-node-expanded";
32731                 }else{
32732                     cls += "-plus";
32733                     c1 = "x-tree-node-expanded";
32734                     c2 = "x-tree-node-collapsed";
32735                 }
32736                 if(this.wasLeaf){
32737                     this.removeClass("x-tree-node-leaf");
32738                     this.wasLeaf = false;
32739                 }
32740                 if(this.c1 != c1 || this.c2 != c2){
32741                     Roo.fly(this.elNode).replaceClass(c1, c2);
32742                     this.c1 = c1; this.c2 = c2;
32743                 }
32744             }else{
32745                 // this changes non-leafs into leafs if they have no children.
32746                 // it's not very rational behaviour..
32747                 
32748                 if(!this.wasLeaf && this.node.leaf){
32749                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32750                     delete this.c1;
32751                     delete this.c2;
32752                     this.wasLeaf = true;
32753                 }
32754             }
32755             var ecc = "x-tree-ec-icon "+cls;
32756             if(this.ecc != ecc){
32757                 this.ecNode.className = ecc;
32758                 this.ecc = ecc;
32759             }
32760         }
32761     },
32762
32763     getChildIndent : function(){
32764         if(!this.childIndent){
32765             var buf = [];
32766             var p = this.node;
32767             while(p){
32768                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32769                     if(!p.isLast()) {
32770                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32771                     } else {
32772                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32773                     }
32774                 }
32775                 p = p.parentNode;
32776             }
32777             this.childIndent = buf.join("");
32778         }
32779         return this.childIndent;
32780     },
32781
32782     renderIndent : function(){
32783         if(this.rendered){
32784             var indent = "";
32785             var p = this.node.parentNode;
32786             if(p){
32787                 indent = p.ui.getChildIndent();
32788             }
32789             if(this.indentMarkup != indent){ // don't rerender if not required
32790                 this.indentNode.innerHTML = indent;
32791                 this.indentMarkup = indent;
32792             }
32793             this.updateExpandIcon();
32794         }
32795     }
32796 };
32797
32798 Roo.tree.RootTreeNodeUI = function(){
32799     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32800 };
32801 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32802     render : function(){
32803         if(!this.rendered){
32804             var targetNode = this.node.ownerTree.innerCt.dom;
32805             this.node.expanded = true;
32806             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32807             this.wrap = this.ctNode = targetNode.firstChild;
32808         }
32809     },
32810     collapse : function(){
32811     },
32812     expand : function(){
32813     }
32814 });/*
32815  * Based on:
32816  * Ext JS Library 1.1.1
32817  * Copyright(c) 2006-2007, Ext JS, LLC.
32818  *
32819  * Originally Released Under LGPL - original licence link has changed is not relivant.
32820  *
32821  * Fork - LGPL
32822  * <script type="text/javascript">
32823  */
32824 /**
32825  * @class Roo.tree.TreeLoader
32826  * @extends Roo.util.Observable
32827  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32828  * nodes from a specified URL. The response must be a javascript Array definition
32829  * who's elements are node definition objects. eg:
32830  * <pre><code>
32831    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32832     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32833 </code></pre>
32834  * <br><br>
32835  * A server request is sent, and child nodes are loaded only when a node is expanded.
32836  * The loading node's id is passed to the server under the parameter name "node" to
32837  * enable the server to produce the correct child nodes.
32838  * <br><br>
32839  * To pass extra parameters, an event handler may be attached to the "beforeload"
32840  * event, and the parameters specified in the TreeLoader's baseParams property:
32841  * <pre><code>
32842     myTreeLoader.on("beforeload", function(treeLoader, node) {
32843         this.baseParams.category = node.attributes.category;
32844     }, this);
32845 </code></pre><
32846  * This would pass an HTTP parameter called "category" to the server containing
32847  * the value of the Node's "category" attribute.
32848  * @constructor
32849  * Creates a new Treeloader.
32850  * @param {Object} config A config object containing config properties.
32851  */
32852 Roo.tree.TreeLoader = function(config){
32853     this.baseParams = {};
32854     this.requestMethod = "POST";
32855     Roo.apply(this, config);
32856
32857     this.addEvents({
32858     
32859         /**
32860          * @event beforeload
32861          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32862          * @param {Object} This TreeLoader object.
32863          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32864          * @param {Object} callback The callback function specified in the {@link #load} call.
32865          */
32866         beforeload : true,
32867         /**
32868          * @event load
32869          * Fires when the node has been successfuly loaded.
32870          * @param {Object} This TreeLoader object.
32871          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32872          * @param {Object} response The response object containing the data from the server.
32873          */
32874         load : true,
32875         /**
32876          * @event loadexception
32877          * Fires if the network request failed.
32878          * @param {Object} This TreeLoader object.
32879          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32880          * @param {Object} response The response object containing the data from the server.
32881          */
32882         loadexception : true,
32883         /**
32884          * @event create
32885          * Fires before a node is created, enabling you to return custom Node types 
32886          * @param {Object} This TreeLoader object.
32887          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32888          */
32889         create : true
32890     });
32891
32892     Roo.tree.TreeLoader.superclass.constructor.call(this);
32893 };
32894
32895 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32896     /**
32897     * @cfg {String} dataUrl The URL from which to request a Json string which
32898     * specifies an array of node definition object representing the child nodes
32899     * to be loaded.
32900     */
32901     /**
32902     * @cfg {Object} baseParams (optional) An object containing properties which
32903     * specify HTTP parameters to be passed to each request for child nodes.
32904     */
32905     /**
32906     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32907     * created by this loader. If the attributes sent by the server have an attribute in this object,
32908     * they take priority.
32909     */
32910     /**
32911     * @cfg {Object} uiProviders (optional) An object containing properties which
32912     * 
32913     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32914     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32915     * <i>uiProvider</i> attribute of a returned child node is a string rather
32916     * than a reference to a TreeNodeUI implementation, this that string value
32917     * is used as a property name in the uiProviders object. You can define the provider named
32918     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32919     */
32920     uiProviders : {},
32921
32922     /**
32923     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32924     * child nodes before loading.
32925     */
32926     clearOnLoad : true,
32927
32928     /**
32929     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32930     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32931     * Grid query { data : [ .....] }
32932     */
32933     
32934     root : false,
32935      /**
32936     * @cfg {String} queryParam (optional) 
32937     * Name of the query as it will be passed on the querystring (defaults to 'node')
32938     * eg. the request will be ?node=[id]
32939     */
32940     
32941     
32942     queryParam: false,
32943     
32944     /**
32945      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32946      * This is called automatically when a node is expanded, but may be used to reload
32947      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32948      * @param {Roo.tree.TreeNode} node
32949      * @param {Function} callback
32950      */
32951     load : function(node, callback){
32952         if(this.clearOnLoad){
32953             while(node.firstChild){
32954                 node.removeChild(node.firstChild);
32955             }
32956         }
32957         if(node.attributes.children){ // preloaded json children
32958             var cs = node.attributes.children;
32959             for(var i = 0, len = cs.length; i < len; i++){
32960                 node.appendChild(this.createNode(cs[i]));
32961             }
32962             if(typeof callback == "function"){
32963                 callback();
32964             }
32965         }else if(this.dataUrl){
32966             this.requestData(node, callback);
32967         }
32968     },
32969
32970     getParams: function(node){
32971         var buf = [], bp = this.baseParams;
32972         for(var key in bp){
32973             if(typeof bp[key] != "function"){
32974                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32975             }
32976         }
32977         var n = this.queryParam === false ? 'node' : this.queryParam;
32978         buf.push(n + "=", encodeURIComponent(node.id));
32979         return buf.join("");
32980     },
32981
32982     requestData : function(node, callback){
32983         if(this.fireEvent("beforeload", this, node, callback) !== false){
32984             this.transId = Roo.Ajax.request({
32985                 method:this.requestMethod,
32986                 url: this.dataUrl||this.url,
32987                 success: this.handleResponse,
32988                 failure: this.handleFailure,
32989                 scope: this,
32990                 argument: {callback: callback, node: node},
32991                 params: this.getParams(node)
32992             });
32993         }else{
32994             // if the load is cancelled, make sure we notify
32995             // the node that we are done
32996             if(typeof callback == "function"){
32997                 callback();
32998             }
32999         }
33000     },
33001
33002     isLoading : function(){
33003         return this.transId ? true : false;
33004     },
33005
33006     abort : function(){
33007         if(this.isLoading()){
33008             Roo.Ajax.abort(this.transId);
33009         }
33010     },
33011
33012     // private
33013     createNode : function(attr)
33014     {
33015         // apply baseAttrs, nice idea Corey!
33016         if(this.baseAttrs){
33017             Roo.applyIf(attr, this.baseAttrs);
33018         }
33019         if(this.applyLoader !== false){
33020             attr.loader = this;
33021         }
33022         // uiProvider = depreciated..
33023         
33024         if(typeof(attr.uiProvider) == 'string'){
33025            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33026                 /**  eval:var:attr */ eval(attr.uiProvider);
33027         }
33028         if(typeof(this.uiProviders['default']) != 'undefined') {
33029             attr.uiProvider = this.uiProviders['default'];
33030         }
33031         
33032         this.fireEvent('create', this, attr);
33033         
33034         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33035         return(attr.leaf ?
33036                         new Roo.tree.TreeNode(attr) :
33037                         new Roo.tree.AsyncTreeNode(attr));
33038     },
33039
33040     processResponse : function(response, node, callback)
33041     {
33042         var json = response.responseText;
33043         try {
33044             
33045             var o = Roo.decode(json);
33046             
33047             if (!o.success) {
33048                 // it's a failure condition.
33049                 var a = response.argument;
33050                 this.fireEvent("loadexception", this, a.node, response);
33051                 Roo.log("Load failed - should have a handler really");
33052                 return;
33053             }
33054             
33055             if (this.root !== false) {
33056                 o = o[this.root];
33057             }
33058             
33059             for(var i = 0, len = o.length; i < len; i++){
33060                 var n = this.createNode(o[i]);
33061                 if(n){
33062                     node.appendChild(n);
33063                 }
33064             }
33065             if(typeof callback == "function"){
33066                 callback(this, node);
33067             }
33068         }catch(e){
33069             this.handleFailure(response);
33070         }
33071     },
33072
33073     handleResponse : function(response){
33074         this.transId = false;
33075         var a = response.argument;
33076         this.processResponse(response, a.node, a.callback);
33077         this.fireEvent("load", this, a.node, response);
33078     },
33079
33080     handleFailure : function(response)
33081     {
33082         // should handle failure better..
33083         this.transId = false;
33084         var a = response.argument;
33085         this.fireEvent("loadexception", this, a.node, response);
33086         if(typeof a.callback == "function"){
33087             a.callback(this, a.node);
33088         }
33089     }
33090 });/*
33091  * Based on:
33092  * Ext JS Library 1.1.1
33093  * Copyright(c) 2006-2007, Ext JS, LLC.
33094  *
33095  * Originally Released Under LGPL - original licence link has changed is not relivant.
33096  *
33097  * Fork - LGPL
33098  * <script type="text/javascript">
33099  */
33100
33101 /**
33102 * @class Roo.tree.TreeFilter
33103 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33104 * @param {TreePanel} tree
33105 * @param {Object} config (optional)
33106  */
33107 Roo.tree.TreeFilter = function(tree, config){
33108     this.tree = tree;
33109     this.filtered = {};
33110     Roo.apply(this, config);
33111 };
33112
33113 Roo.tree.TreeFilter.prototype = {
33114     clearBlank:false,
33115     reverse:false,
33116     autoClear:false,
33117     remove:false,
33118
33119      /**
33120      * Filter the data by a specific attribute.
33121      * @param {String/RegExp} value Either string that the attribute value
33122      * should start with or a RegExp to test against the attribute
33123      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33124      * @param {TreeNode} startNode (optional) The node to start the filter at.
33125      */
33126     filter : function(value, attr, startNode){
33127         attr = attr || "text";
33128         var f;
33129         if(typeof value == "string"){
33130             var vlen = value.length;
33131             // auto clear empty filter
33132             if(vlen == 0 && this.clearBlank){
33133                 this.clear();
33134                 return;
33135             }
33136             value = value.toLowerCase();
33137             f = function(n){
33138                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33139             };
33140         }else if(value.exec){ // regex?
33141             f = function(n){
33142                 return value.test(n.attributes[attr]);
33143             };
33144         }else{
33145             throw 'Illegal filter type, must be string or regex';
33146         }
33147         this.filterBy(f, null, startNode);
33148         },
33149
33150     /**
33151      * Filter by a function. The passed function will be called with each
33152      * node in the tree (or from the startNode). If the function returns true, the node is kept
33153      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33154      * @param {Function} fn The filter function
33155      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33156      */
33157     filterBy : function(fn, scope, startNode){
33158         startNode = startNode || this.tree.root;
33159         if(this.autoClear){
33160             this.clear();
33161         }
33162         var af = this.filtered, rv = this.reverse;
33163         var f = function(n){
33164             if(n == startNode){
33165                 return true;
33166             }
33167             if(af[n.id]){
33168                 return false;
33169             }
33170             var m = fn.call(scope || n, n);
33171             if(!m || rv){
33172                 af[n.id] = n;
33173                 n.ui.hide();
33174                 return false;
33175             }
33176             return true;
33177         };
33178         startNode.cascade(f);
33179         if(this.remove){
33180            for(var id in af){
33181                if(typeof id != "function"){
33182                    var n = af[id];
33183                    if(n && n.parentNode){
33184                        n.parentNode.removeChild(n);
33185                    }
33186                }
33187            }
33188         }
33189     },
33190
33191     /**
33192      * Clears the current filter. Note: with the "remove" option
33193      * set a filter cannot be cleared.
33194      */
33195     clear : function(){
33196         var t = this.tree;
33197         var af = this.filtered;
33198         for(var id in af){
33199             if(typeof id != "function"){
33200                 var n = af[id];
33201                 if(n){
33202                     n.ui.show();
33203                 }
33204             }
33205         }
33206         this.filtered = {};
33207     }
33208 };
33209 /*
33210  * Based on:
33211  * Ext JS Library 1.1.1
33212  * Copyright(c) 2006-2007, Ext JS, LLC.
33213  *
33214  * Originally Released Under LGPL - original licence link has changed is not relivant.
33215  *
33216  * Fork - LGPL
33217  * <script type="text/javascript">
33218  */
33219  
33220
33221 /**
33222  * @class Roo.tree.TreeSorter
33223  * Provides sorting of nodes in a TreePanel
33224  * 
33225  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33226  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33227  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33228  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33229  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33230  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33231  * @constructor
33232  * @param {TreePanel} tree
33233  * @param {Object} config
33234  */
33235 Roo.tree.TreeSorter = function(tree, config){
33236     Roo.apply(this, config);
33237     tree.on("beforechildrenrendered", this.doSort, this);
33238     tree.on("append", this.updateSort, this);
33239     tree.on("insert", this.updateSort, this);
33240     
33241     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33242     var p = this.property || "text";
33243     var sortType = this.sortType;
33244     var fs = this.folderSort;
33245     var cs = this.caseSensitive === true;
33246     var leafAttr = this.leafAttr || 'leaf';
33247
33248     this.sortFn = function(n1, n2){
33249         if(fs){
33250             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33251                 return 1;
33252             }
33253             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33254                 return -1;
33255             }
33256         }
33257         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33258         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33259         if(v1 < v2){
33260                         return dsc ? +1 : -1;
33261                 }else if(v1 > v2){
33262                         return dsc ? -1 : +1;
33263         }else{
33264                 return 0;
33265         }
33266     };
33267 };
33268
33269 Roo.tree.TreeSorter.prototype = {
33270     doSort : function(node){
33271         node.sort(this.sortFn);
33272     },
33273     
33274     compareNodes : function(n1, n2){
33275         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33276     },
33277     
33278     updateSort : function(tree, node){
33279         if(node.childrenRendered){
33280             this.doSort.defer(1, this, [node]);
33281         }
33282     }
33283 };/*
33284  * Based on:
33285  * Ext JS Library 1.1.1
33286  * Copyright(c) 2006-2007, Ext JS, LLC.
33287  *
33288  * Originally Released Under LGPL - original licence link has changed is not relivant.
33289  *
33290  * Fork - LGPL
33291  * <script type="text/javascript">
33292  */
33293
33294 if(Roo.dd.DropZone){
33295     
33296 Roo.tree.TreeDropZone = function(tree, config){
33297     this.allowParentInsert = false;
33298     this.allowContainerDrop = false;
33299     this.appendOnly = false;
33300     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33301     this.tree = tree;
33302     this.lastInsertClass = "x-tree-no-status";
33303     this.dragOverData = {};
33304 };
33305
33306 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33307     ddGroup : "TreeDD",
33308     
33309     expandDelay : 1000,
33310     
33311     expandNode : function(node){
33312         if(node.hasChildNodes() && !node.isExpanded()){
33313             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33314         }
33315     },
33316     
33317     queueExpand : function(node){
33318         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33319     },
33320     
33321     cancelExpand : function(){
33322         if(this.expandProcId){
33323             clearTimeout(this.expandProcId);
33324             this.expandProcId = false;
33325         }
33326     },
33327     
33328     isValidDropPoint : function(n, pt, dd, e, data){
33329         if(!n || !data){ return false; }
33330         var targetNode = n.node;
33331         var dropNode = data.node;
33332         // default drop rules
33333         if(!(targetNode && targetNode.isTarget && pt)){
33334             return false;
33335         }
33336         if(pt == "append" && targetNode.allowChildren === false){
33337             return false;
33338         }
33339         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33340             return false;
33341         }
33342         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33343             return false;
33344         }
33345         // reuse the object
33346         var overEvent = this.dragOverData;
33347         overEvent.tree = this.tree;
33348         overEvent.target = targetNode;
33349         overEvent.data = data;
33350         overEvent.point = pt;
33351         overEvent.source = dd;
33352         overEvent.rawEvent = e;
33353         overEvent.dropNode = dropNode;
33354         overEvent.cancel = false;  
33355         var result = this.tree.fireEvent("nodedragover", overEvent);
33356         return overEvent.cancel === false && result !== false;
33357     },
33358     
33359     getDropPoint : function(e, n, dd){
33360         var tn = n.node;
33361         if(tn.isRoot){
33362             return tn.allowChildren !== false ? "append" : false; // always append for root
33363         }
33364         var dragEl = n.ddel;
33365         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33366         var y = Roo.lib.Event.getPageY(e);
33367         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33368         
33369         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33370         var noAppend = tn.allowChildren === false;
33371         if(this.appendOnly || tn.parentNode.allowChildren === false){
33372             return noAppend ? false : "append";
33373         }
33374         var noBelow = false;
33375         if(!this.allowParentInsert){
33376             noBelow = tn.hasChildNodes() && tn.isExpanded();
33377         }
33378         var q = (b - t) / (noAppend ? 2 : 3);
33379         if(y >= t && y < (t + q)){
33380             return "above";
33381         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33382             return "below";
33383         }else{
33384             return "append";
33385         }
33386     },
33387     
33388     onNodeEnter : function(n, dd, e, data){
33389         this.cancelExpand();
33390     },
33391     
33392     onNodeOver : function(n, dd, e, data){
33393         var pt = this.getDropPoint(e, n, dd);
33394         var node = n.node;
33395         
33396         // auto node expand check
33397         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33398             this.queueExpand(node);
33399         }else if(pt != "append"){
33400             this.cancelExpand();
33401         }
33402         
33403         // set the insert point style on the target node
33404         var returnCls = this.dropNotAllowed;
33405         if(this.isValidDropPoint(n, pt, dd, e, data)){
33406            if(pt){
33407                var el = n.ddel;
33408                var cls;
33409                if(pt == "above"){
33410                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33411                    cls = "x-tree-drag-insert-above";
33412                }else if(pt == "below"){
33413                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33414                    cls = "x-tree-drag-insert-below";
33415                }else{
33416                    returnCls = "x-tree-drop-ok-append";
33417                    cls = "x-tree-drag-append";
33418                }
33419                if(this.lastInsertClass != cls){
33420                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33421                    this.lastInsertClass = cls;
33422                }
33423            }
33424        }
33425        return returnCls;
33426     },
33427     
33428     onNodeOut : function(n, dd, e, data){
33429         this.cancelExpand();
33430         this.removeDropIndicators(n);
33431     },
33432     
33433     onNodeDrop : function(n, dd, e, data){
33434         var point = this.getDropPoint(e, n, dd);
33435         var targetNode = n.node;
33436         targetNode.ui.startDrop();
33437         if(!this.isValidDropPoint(n, point, dd, e, data)){
33438             targetNode.ui.endDrop();
33439             return false;
33440         }
33441         // first try to find the drop node
33442         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33443         var dropEvent = {
33444             tree : this.tree,
33445             target: targetNode,
33446             data: data,
33447             point: point,
33448             source: dd,
33449             rawEvent: e,
33450             dropNode: dropNode,
33451             cancel: !dropNode   
33452         };
33453         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33454         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33455             targetNode.ui.endDrop();
33456             return false;
33457         }
33458         // allow target changing
33459         targetNode = dropEvent.target;
33460         if(point == "append" && !targetNode.isExpanded()){
33461             targetNode.expand(false, null, function(){
33462                 this.completeDrop(dropEvent);
33463             }.createDelegate(this));
33464         }else{
33465             this.completeDrop(dropEvent);
33466         }
33467         return true;
33468     },
33469     
33470     completeDrop : function(de){
33471         var ns = de.dropNode, p = de.point, t = de.target;
33472         if(!(ns instanceof Array)){
33473             ns = [ns];
33474         }
33475         var n;
33476         for(var i = 0, len = ns.length; i < len; i++){
33477             n = ns[i];
33478             if(p == "above"){
33479                 t.parentNode.insertBefore(n, t);
33480             }else if(p == "below"){
33481                 t.parentNode.insertBefore(n, t.nextSibling);
33482             }else{
33483                 t.appendChild(n);
33484             }
33485         }
33486         n.ui.focus();
33487         if(this.tree.hlDrop){
33488             n.ui.highlight();
33489         }
33490         t.ui.endDrop();
33491         this.tree.fireEvent("nodedrop", de);
33492     },
33493     
33494     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33495         if(this.tree.hlDrop){
33496             dropNode.ui.focus();
33497             dropNode.ui.highlight();
33498         }
33499         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33500     },
33501     
33502     getTree : function(){
33503         return this.tree;
33504     },
33505     
33506     removeDropIndicators : function(n){
33507         if(n && n.ddel){
33508             var el = n.ddel;
33509             Roo.fly(el).removeClass([
33510                     "x-tree-drag-insert-above",
33511                     "x-tree-drag-insert-below",
33512                     "x-tree-drag-append"]);
33513             this.lastInsertClass = "_noclass";
33514         }
33515     },
33516     
33517     beforeDragDrop : function(target, e, id){
33518         this.cancelExpand();
33519         return true;
33520     },
33521     
33522     afterRepair : function(data){
33523         if(data && Roo.enableFx){
33524             data.node.ui.highlight();
33525         }
33526         this.hideProxy();
33527     }    
33528 });
33529
33530 }
33531 /*
33532  * Based on:
33533  * Ext JS Library 1.1.1
33534  * Copyright(c) 2006-2007, Ext JS, LLC.
33535  *
33536  * Originally Released Under LGPL - original licence link has changed is not relivant.
33537  *
33538  * Fork - LGPL
33539  * <script type="text/javascript">
33540  */
33541  
33542
33543 if(Roo.dd.DragZone){
33544 Roo.tree.TreeDragZone = function(tree, config){
33545     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33546     this.tree = tree;
33547 };
33548
33549 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33550     ddGroup : "TreeDD",
33551     
33552     onBeforeDrag : function(data, e){
33553         var n = data.node;
33554         return n && n.draggable && !n.disabled;
33555     },
33556     
33557     onInitDrag : function(e){
33558         var data = this.dragData;
33559         this.tree.getSelectionModel().select(data.node);
33560         this.proxy.update("");
33561         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33562         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33563     },
33564     
33565     getRepairXY : function(e, data){
33566         return data.node.ui.getDDRepairXY();
33567     },
33568     
33569     onEndDrag : function(data, e){
33570         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33571     },
33572     
33573     onValidDrop : function(dd, e, id){
33574         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33575         this.hideProxy();
33576     },
33577     
33578     beforeInvalidDrop : function(e, id){
33579         // this scrolls the original position back into view
33580         var sm = this.tree.getSelectionModel();
33581         sm.clearSelections();
33582         sm.select(this.dragData.node);
33583     }
33584 });
33585 }/*
33586  * Based on:
33587  * Ext JS Library 1.1.1
33588  * Copyright(c) 2006-2007, Ext JS, LLC.
33589  *
33590  * Originally Released Under LGPL - original licence link has changed is not relivant.
33591  *
33592  * Fork - LGPL
33593  * <script type="text/javascript">
33594  */
33595 /**
33596  * @class Roo.tree.TreeEditor
33597  * @extends Roo.Editor
33598  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33599  * as the editor field.
33600  * @constructor
33601  * @param {Object} config (used to be the tree panel.)
33602  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33603  * 
33604  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33605  * @cfg {Roo.form.TextField|Object} field The field configuration
33606  *
33607  * 
33608  */
33609 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33610     var tree = config;
33611     var field;
33612     if (oldconfig) { // old style..
33613         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33614     } else {
33615         // new style..
33616         tree = config.tree;
33617         config.field = config.field  || {};
33618         config.field.xtype = 'TextField';
33619         field = Roo.factory(config.field, Roo.form);
33620     }
33621     config = config || {};
33622     
33623     
33624     this.addEvents({
33625         /**
33626          * @event beforenodeedit
33627          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33628          * false from the handler of this event.
33629          * @param {Editor} this
33630          * @param {Roo.tree.Node} node 
33631          */
33632         "beforenodeedit" : true
33633     });
33634     
33635     //Roo.log(config);
33636     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33637
33638     this.tree = tree;
33639
33640     tree.on('beforeclick', this.beforeNodeClick, this);
33641     tree.getTreeEl().on('mousedown', this.hide, this);
33642     this.on('complete', this.updateNode, this);
33643     this.on('beforestartedit', this.fitToTree, this);
33644     this.on('startedit', this.bindScroll, this, {delay:10});
33645     this.on('specialkey', this.onSpecialKey, this);
33646 };
33647
33648 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33649     /**
33650      * @cfg {String} alignment
33651      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33652      */
33653     alignment: "l-l",
33654     // inherit
33655     autoSize: false,
33656     /**
33657      * @cfg {Boolean} hideEl
33658      * True to hide the bound element while the editor is displayed (defaults to false)
33659      */
33660     hideEl : false,
33661     /**
33662      * @cfg {String} cls
33663      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33664      */
33665     cls: "x-small-editor x-tree-editor",
33666     /**
33667      * @cfg {Boolean} shim
33668      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33669      */
33670     shim:false,
33671     // inherit
33672     shadow:"frame",
33673     /**
33674      * @cfg {Number} maxWidth
33675      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33676      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33677      * scroll and client offsets into account prior to each edit.
33678      */
33679     maxWidth: 250,
33680
33681     editDelay : 350,
33682
33683     // private
33684     fitToTree : function(ed, el){
33685         var td = this.tree.getTreeEl().dom, nd = el.dom;
33686         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33687             td.scrollLeft = nd.offsetLeft;
33688         }
33689         var w = Math.min(
33690                 this.maxWidth,
33691                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33692         this.setSize(w, '');
33693         
33694         return this.fireEvent('beforenodeedit', this, this.editNode);
33695         
33696     },
33697
33698     // private
33699     triggerEdit : function(node){
33700         this.completeEdit();
33701         this.editNode = node;
33702         this.startEdit(node.ui.textNode, node.text);
33703     },
33704
33705     // private
33706     bindScroll : function(){
33707         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33708     },
33709
33710     // private
33711     beforeNodeClick : function(node, e){
33712         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33713         this.lastClick = new Date();
33714         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33715             e.stopEvent();
33716             this.triggerEdit(node);
33717             return false;
33718         }
33719         return true;
33720     },
33721
33722     // private
33723     updateNode : function(ed, value){
33724         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33725         this.editNode.setText(value);
33726     },
33727
33728     // private
33729     onHide : function(){
33730         Roo.tree.TreeEditor.superclass.onHide.call(this);
33731         if(this.editNode){
33732             this.editNode.ui.focus();
33733         }
33734     },
33735
33736     // private
33737     onSpecialKey : function(field, e){
33738         var k = e.getKey();
33739         if(k == e.ESC){
33740             e.stopEvent();
33741             this.cancelEdit();
33742         }else if(k == e.ENTER && !e.hasModifier()){
33743             e.stopEvent();
33744             this.completeEdit();
33745         }
33746     }
33747 });//<Script type="text/javascript">
33748 /*
33749  * Based on:
33750  * Ext JS Library 1.1.1
33751  * Copyright(c) 2006-2007, Ext JS, LLC.
33752  *
33753  * Originally Released Under LGPL - original licence link has changed is not relivant.
33754  *
33755  * Fork - LGPL
33756  * <script type="text/javascript">
33757  */
33758  
33759 /**
33760  * Not documented??? - probably should be...
33761  */
33762
33763 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33764     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33765     
33766     renderElements : function(n, a, targetNode, bulkRender){
33767         //consel.log("renderElements?");
33768         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33769
33770         var t = n.getOwnerTree();
33771         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33772         
33773         var cols = t.columns;
33774         var bw = t.borderWidth;
33775         var c = cols[0];
33776         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33777          var cb = typeof a.checked == "boolean";
33778         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33779         var colcls = 'x-t-' + tid + '-c0';
33780         var buf = [
33781             '<li class="x-tree-node">',
33782             
33783                 
33784                 '<div class="x-tree-node-el ', a.cls,'">',
33785                     // extran...
33786                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33787                 
33788                 
33789                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33790                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33791                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33792                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33793                            (a.iconCls ? ' '+a.iconCls : ''),
33794                            '" unselectable="on" />',
33795                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33796                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33797                              
33798                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33799                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33800                             '<span unselectable="on" qtip="' + tx + '">',
33801                              tx,
33802                              '</span></a>' ,
33803                     '</div>',
33804                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33805                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33806                  ];
33807         for(var i = 1, len = cols.length; i < len; i++){
33808             c = cols[i];
33809             colcls = 'x-t-' + tid + '-c' +i;
33810             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33811             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33812                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33813                       "</div>");
33814          }
33815          
33816          buf.push(
33817             '</a>',
33818             '<div class="x-clear"></div></div>',
33819             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33820             "</li>");
33821         
33822         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33823             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33824                                 n.nextSibling.ui.getEl(), buf.join(""));
33825         }else{
33826             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33827         }
33828         var el = this.wrap.firstChild;
33829         this.elRow = el;
33830         this.elNode = el.firstChild;
33831         this.ranchor = el.childNodes[1];
33832         this.ctNode = this.wrap.childNodes[1];
33833         var cs = el.firstChild.childNodes;
33834         this.indentNode = cs[0];
33835         this.ecNode = cs[1];
33836         this.iconNode = cs[2];
33837         var index = 3;
33838         if(cb){
33839             this.checkbox = cs[3];
33840             index++;
33841         }
33842         this.anchor = cs[index];
33843         
33844         this.textNode = cs[index].firstChild;
33845         
33846         //el.on("click", this.onClick, this);
33847         //el.on("dblclick", this.onDblClick, this);
33848         
33849         
33850        // console.log(this);
33851     },
33852     initEvents : function(){
33853         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33854         
33855             
33856         var a = this.ranchor;
33857
33858         var el = Roo.get(a);
33859
33860         if(Roo.isOpera){ // opera render bug ignores the CSS
33861             el.setStyle("text-decoration", "none");
33862         }
33863
33864         el.on("click", this.onClick, this);
33865         el.on("dblclick", this.onDblClick, this);
33866         el.on("contextmenu", this.onContextMenu, this);
33867         
33868     },
33869     
33870     /*onSelectedChange : function(state){
33871         if(state){
33872             this.focus();
33873             this.addClass("x-tree-selected");
33874         }else{
33875             //this.blur();
33876             this.removeClass("x-tree-selected");
33877         }
33878     },*/
33879     addClass : function(cls){
33880         if(this.elRow){
33881             Roo.fly(this.elRow).addClass(cls);
33882         }
33883         
33884     },
33885     
33886     
33887     removeClass : function(cls){
33888         if(this.elRow){
33889             Roo.fly(this.elRow).removeClass(cls);
33890         }
33891     }
33892
33893     
33894     
33895 });//<Script type="text/javascript">
33896
33897 /*
33898  * Based on:
33899  * Ext JS Library 1.1.1
33900  * Copyright(c) 2006-2007, Ext JS, LLC.
33901  *
33902  * Originally Released Under LGPL - original licence link has changed is not relivant.
33903  *
33904  * Fork - LGPL
33905  * <script type="text/javascript">
33906  */
33907  
33908
33909 /**
33910  * @class Roo.tree.ColumnTree
33911  * @extends Roo.data.TreePanel
33912  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33913  * @cfg {int} borderWidth  compined right/left border allowance
33914  * @constructor
33915  * @param {String/HTMLElement/Element} el The container element
33916  * @param {Object} config
33917  */
33918 Roo.tree.ColumnTree =  function(el, config)
33919 {
33920    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33921    this.addEvents({
33922         /**
33923         * @event resize
33924         * Fire this event on a container when it resizes
33925         * @param {int} w Width
33926         * @param {int} h Height
33927         */
33928        "resize" : true
33929     });
33930     this.on('resize', this.onResize, this);
33931 };
33932
33933 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33934     //lines:false,
33935     
33936     
33937     borderWidth: Roo.isBorderBox ? 0 : 2, 
33938     headEls : false,
33939     
33940     render : function(){
33941         // add the header.....
33942        
33943         Roo.tree.ColumnTree.superclass.render.apply(this);
33944         
33945         this.el.addClass('x-column-tree');
33946         
33947         this.headers = this.el.createChild(
33948             {cls:'x-tree-headers'},this.innerCt.dom);
33949    
33950         var cols = this.columns, c;
33951         var totalWidth = 0;
33952         this.headEls = [];
33953         var  len = cols.length;
33954         for(var i = 0; i < len; i++){
33955              c = cols[i];
33956              totalWidth += c.width;
33957             this.headEls.push(this.headers.createChild({
33958                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33959                  cn: {
33960                      cls:'x-tree-hd-text',
33961                      html: c.header
33962                  },
33963                  style:'width:'+(c.width-this.borderWidth)+'px;'
33964              }));
33965         }
33966         this.headers.createChild({cls:'x-clear'});
33967         // prevent floats from wrapping when clipped
33968         this.headers.setWidth(totalWidth);
33969         //this.innerCt.setWidth(totalWidth);
33970         this.innerCt.setStyle({ overflow: 'auto' });
33971         this.onResize(this.width, this.height);
33972              
33973         
33974     },
33975     onResize : function(w,h)
33976     {
33977         this.height = h;
33978         this.width = w;
33979         // resize cols..
33980         this.innerCt.setWidth(this.width);
33981         this.innerCt.setHeight(this.height-20);
33982         
33983         // headers...
33984         var cols = this.columns, c;
33985         var totalWidth = 0;
33986         var expEl = false;
33987         var len = cols.length;
33988         for(var i = 0; i < len; i++){
33989             c = cols[i];
33990             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33991                 // it's the expander..
33992                 expEl  = this.headEls[i];
33993                 continue;
33994             }
33995             totalWidth += c.width;
33996             
33997         }
33998         if (expEl) {
33999             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34000         }
34001         this.headers.setWidth(w-20);
34002
34003         
34004         
34005         
34006     }
34007 });
34008 /*
34009  * Based on:
34010  * Ext JS Library 1.1.1
34011  * Copyright(c) 2006-2007, Ext JS, LLC.
34012  *
34013  * Originally Released Under LGPL - original licence link has changed is not relivant.
34014  *
34015  * Fork - LGPL
34016  * <script type="text/javascript">
34017  */
34018  
34019 /**
34020  * @class Roo.menu.Menu
34021  * @extends Roo.util.Observable
34022  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34023  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34024  * @constructor
34025  * Creates a new Menu
34026  * @param {Object} config Configuration options
34027  */
34028 Roo.menu.Menu = function(config){
34029     Roo.apply(this, config);
34030     this.id = this.id || Roo.id();
34031     this.addEvents({
34032         /**
34033          * @event beforeshow
34034          * Fires before this menu is displayed
34035          * @param {Roo.menu.Menu} this
34036          */
34037         beforeshow : true,
34038         /**
34039          * @event beforehide
34040          * Fires before this menu is hidden
34041          * @param {Roo.menu.Menu} this
34042          */
34043         beforehide : true,
34044         /**
34045          * @event show
34046          * Fires after this menu is displayed
34047          * @param {Roo.menu.Menu} this
34048          */
34049         show : true,
34050         /**
34051          * @event hide
34052          * Fires after this menu is hidden
34053          * @param {Roo.menu.Menu} this
34054          */
34055         hide : true,
34056         /**
34057          * @event click
34058          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34059          * @param {Roo.menu.Menu} this
34060          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34061          * @param {Roo.EventObject} e
34062          */
34063         click : true,
34064         /**
34065          * @event mouseover
34066          * Fires when the mouse is hovering over this menu
34067          * @param {Roo.menu.Menu} this
34068          * @param {Roo.EventObject} e
34069          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34070          */
34071         mouseover : true,
34072         /**
34073          * @event mouseout
34074          * Fires when the mouse exits this menu
34075          * @param {Roo.menu.Menu} this
34076          * @param {Roo.EventObject} e
34077          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34078          */
34079         mouseout : true,
34080         /**
34081          * @event itemclick
34082          * Fires when a menu item contained in this menu is clicked
34083          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34084          * @param {Roo.EventObject} e
34085          */
34086         itemclick: true
34087     });
34088     if (this.registerMenu) {
34089         Roo.menu.MenuMgr.register(this);
34090     }
34091     
34092     var mis = this.items;
34093     this.items = new Roo.util.MixedCollection();
34094     if(mis){
34095         this.add.apply(this, mis);
34096     }
34097 };
34098
34099 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34100     /**
34101      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34102      */
34103     minWidth : 120,
34104     /**
34105      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34106      * for bottom-right shadow (defaults to "sides")
34107      */
34108     shadow : "sides",
34109     /**
34110      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34111      * this menu (defaults to "tl-tr?")
34112      */
34113     subMenuAlign : "tl-tr?",
34114     /**
34115      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34116      * relative to its element of origin (defaults to "tl-bl?")
34117      */
34118     defaultAlign : "tl-bl?",
34119     /**
34120      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34121      */
34122     allowOtherMenus : false,
34123     /**
34124      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34125      */
34126     registerMenu : true,
34127
34128     hidden:true,
34129
34130     // private
34131     render : function(){
34132         if(this.el){
34133             return;
34134         }
34135         var el = this.el = new Roo.Layer({
34136             cls: "x-menu",
34137             shadow:this.shadow,
34138             constrain: false,
34139             parentEl: this.parentEl || document.body,
34140             zindex:15000
34141         });
34142
34143         this.keyNav = new Roo.menu.MenuNav(this);
34144
34145         if(this.plain){
34146             el.addClass("x-menu-plain");
34147         }
34148         if(this.cls){
34149             el.addClass(this.cls);
34150         }
34151         // generic focus element
34152         this.focusEl = el.createChild({
34153             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34154         });
34155         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34156         ul.on("click", this.onClick, this);
34157         ul.on("mouseover", this.onMouseOver, this);
34158         ul.on("mouseout", this.onMouseOut, this);
34159         this.items.each(function(item){
34160             var li = document.createElement("li");
34161             li.className = "x-menu-list-item";
34162             ul.dom.appendChild(li);
34163             item.render(li, this);
34164         }, this);
34165         this.ul = ul;
34166         this.autoWidth();
34167     },
34168
34169     // private
34170     autoWidth : function(){
34171         var el = this.el, ul = this.ul;
34172         if(!el){
34173             return;
34174         }
34175         var w = this.width;
34176         if(w){
34177             el.setWidth(w);
34178         }else if(Roo.isIE){
34179             el.setWidth(this.minWidth);
34180             var t = el.dom.offsetWidth; // force recalc
34181             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34182         }
34183     },
34184
34185     // private
34186     delayAutoWidth : function(){
34187         if(this.rendered){
34188             if(!this.awTask){
34189                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34190             }
34191             this.awTask.delay(20);
34192         }
34193     },
34194
34195     // private
34196     findTargetItem : function(e){
34197         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34198         if(t && t.menuItemId){
34199             return this.items.get(t.menuItemId);
34200         }
34201     },
34202
34203     // private
34204     onClick : function(e){
34205         var t;
34206         if(t = this.findTargetItem(e)){
34207             t.onClick(e);
34208             this.fireEvent("click", this, t, e);
34209         }
34210     },
34211
34212     // private
34213     setActiveItem : function(item, autoExpand){
34214         if(item != this.activeItem){
34215             if(this.activeItem){
34216                 this.activeItem.deactivate();
34217             }
34218             this.activeItem = item;
34219             item.activate(autoExpand);
34220         }else if(autoExpand){
34221             item.expandMenu();
34222         }
34223     },
34224
34225     // private
34226     tryActivate : function(start, step){
34227         var items = this.items;
34228         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34229             var item = items.get(i);
34230             if(!item.disabled && item.canActivate){
34231                 this.setActiveItem(item, false);
34232                 return item;
34233             }
34234         }
34235         return false;
34236     },
34237
34238     // private
34239     onMouseOver : function(e){
34240         var t;
34241         if(t = this.findTargetItem(e)){
34242             if(t.canActivate && !t.disabled){
34243                 this.setActiveItem(t, true);
34244             }
34245         }
34246         this.fireEvent("mouseover", this, e, t);
34247     },
34248
34249     // private
34250     onMouseOut : function(e){
34251         var t;
34252         if(t = this.findTargetItem(e)){
34253             if(t == this.activeItem && t.shouldDeactivate(e)){
34254                 this.activeItem.deactivate();
34255                 delete this.activeItem;
34256             }
34257         }
34258         this.fireEvent("mouseout", this, e, t);
34259     },
34260
34261     /**
34262      * Read-only.  Returns true if the menu is currently displayed, else false.
34263      * @type Boolean
34264      */
34265     isVisible : function(){
34266         return this.el && !this.hidden;
34267     },
34268
34269     /**
34270      * Displays this menu relative to another element
34271      * @param {String/HTMLElement/Roo.Element} element The element to align to
34272      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34273      * the element (defaults to this.defaultAlign)
34274      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34275      */
34276     show : function(el, pos, parentMenu){
34277         this.parentMenu = parentMenu;
34278         if(!this.el){
34279             this.render();
34280         }
34281         this.fireEvent("beforeshow", this);
34282         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34283     },
34284
34285     /**
34286      * Displays this menu at a specific xy position
34287      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34289      */
34290     showAt : function(xy, parentMenu, /* private: */_e){
34291         this.parentMenu = parentMenu;
34292         if(!this.el){
34293             this.render();
34294         }
34295         if(_e !== false){
34296             this.fireEvent("beforeshow", this);
34297             xy = this.el.adjustForConstraints(xy);
34298         }
34299         this.el.setXY(xy);
34300         this.el.show();
34301         this.hidden = false;
34302         this.focus();
34303         this.fireEvent("show", this);
34304     },
34305
34306     focus : function(){
34307         if(!this.hidden){
34308             this.doFocus.defer(50, this);
34309         }
34310     },
34311
34312     doFocus : function(){
34313         if(!this.hidden){
34314             this.focusEl.focus();
34315         }
34316     },
34317
34318     /**
34319      * Hides this menu and optionally all parent menus
34320      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34321      */
34322     hide : function(deep){
34323         if(this.el && this.isVisible()){
34324             this.fireEvent("beforehide", this);
34325             if(this.activeItem){
34326                 this.activeItem.deactivate();
34327                 this.activeItem = null;
34328             }
34329             this.el.hide();
34330             this.hidden = true;
34331             this.fireEvent("hide", this);
34332         }
34333         if(deep === true && this.parentMenu){
34334             this.parentMenu.hide(true);
34335         }
34336     },
34337
34338     /**
34339      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34340      * Any of the following are valid:
34341      * <ul>
34342      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34343      * <li>An HTMLElement object which will be converted to a menu item</li>
34344      * <li>A menu item config object that will be created as a new menu item</li>
34345      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34346      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34347      * </ul>
34348      * Usage:
34349      * <pre><code>
34350 // Create the menu
34351 var menu = new Roo.menu.Menu();
34352
34353 // Create a menu item to add by reference
34354 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34355
34356 // Add a bunch of items at once using different methods.
34357 // Only the last item added will be returned.
34358 var item = menu.add(
34359     menuItem,                // add existing item by ref
34360     'Dynamic Item',          // new TextItem
34361     '-',                     // new separator
34362     { text: 'Config Item' }  // new item by config
34363 );
34364 </code></pre>
34365      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34366      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34367      */
34368     add : function(){
34369         var a = arguments, l = a.length, item;
34370         for(var i = 0; i < l; i++){
34371             var el = a[i];
34372             if ((typeof(el) == "object") && el.xtype && el.xns) {
34373                 el = Roo.factory(el, Roo.menu);
34374             }
34375             
34376             if(el.render){ // some kind of Item
34377                 item = this.addItem(el);
34378             }else if(typeof el == "string"){ // string
34379                 if(el == "separator" || el == "-"){
34380                     item = this.addSeparator();
34381                 }else{
34382                     item = this.addText(el);
34383                 }
34384             }else if(el.tagName || el.el){ // element
34385                 item = this.addElement(el);
34386             }else if(typeof el == "object"){ // must be menu item config?
34387                 item = this.addMenuItem(el);
34388             }
34389         }
34390         return item;
34391     },
34392
34393     /**
34394      * Returns this menu's underlying {@link Roo.Element} object
34395      * @return {Roo.Element} The element
34396      */
34397     getEl : function(){
34398         if(!this.el){
34399             this.render();
34400         }
34401         return this.el;
34402     },
34403
34404     /**
34405      * Adds a separator bar to the menu
34406      * @return {Roo.menu.Item} The menu item that was added
34407      */
34408     addSeparator : function(){
34409         return this.addItem(new Roo.menu.Separator());
34410     },
34411
34412     /**
34413      * Adds an {@link Roo.Element} object to the menu
34414      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34415      * @return {Roo.menu.Item} The menu item that was added
34416      */
34417     addElement : function(el){
34418         return this.addItem(new Roo.menu.BaseItem(el));
34419     },
34420
34421     /**
34422      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34423      * @param {Roo.menu.Item} item The menu item to add
34424      * @return {Roo.menu.Item} The menu item that was added
34425      */
34426     addItem : function(item){
34427         this.items.add(item);
34428         if(this.ul){
34429             var li = document.createElement("li");
34430             li.className = "x-menu-list-item";
34431             this.ul.dom.appendChild(li);
34432             item.render(li, this);
34433             this.delayAutoWidth();
34434         }
34435         return item;
34436     },
34437
34438     /**
34439      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34440      * @param {Object} config A MenuItem config object
34441      * @return {Roo.menu.Item} The menu item that was added
34442      */
34443     addMenuItem : function(config){
34444         if(!(config instanceof Roo.menu.Item)){
34445             if(typeof config.checked == "boolean"){ // must be check menu item config?
34446                 config = new Roo.menu.CheckItem(config);
34447             }else{
34448                 config = new Roo.menu.Item(config);
34449             }
34450         }
34451         return this.addItem(config);
34452     },
34453
34454     /**
34455      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34456      * @param {String} text The text to display in the menu item
34457      * @return {Roo.menu.Item} The menu item that was added
34458      */
34459     addText : function(text){
34460         return this.addItem(new Roo.menu.TextItem({ text : text }));
34461     },
34462
34463     /**
34464      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34465      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34466      * @param {Roo.menu.Item} item The menu item to add
34467      * @return {Roo.menu.Item} The menu item that was added
34468      */
34469     insert : function(index, item){
34470         this.items.insert(index, item);
34471         if(this.ul){
34472             var li = document.createElement("li");
34473             li.className = "x-menu-list-item";
34474             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34475             item.render(li, this);
34476             this.delayAutoWidth();
34477         }
34478         return item;
34479     },
34480
34481     /**
34482      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34483      * @param {Roo.menu.Item} item The menu item to remove
34484      */
34485     remove : function(item){
34486         this.items.removeKey(item.id);
34487         item.destroy();
34488     },
34489
34490     /**
34491      * Removes and destroys all items in the menu
34492      */
34493     removeAll : function(){
34494         var f;
34495         while(f = this.items.first()){
34496             this.remove(f);
34497         }
34498     }
34499 });
34500
34501 // MenuNav is a private utility class used internally by the Menu
34502 Roo.menu.MenuNav = function(menu){
34503     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34504     this.scope = this.menu = menu;
34505 };
34506
34507 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34508     doRelay : function(e, h){
34509         var k = e.getKey();
34510         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34511             this.menu.tryActivate(0, 1);
34512             return false;
34513         }
34514         return h.call(this.scope || this, e, this.menu);
34515     },
34516
34517     up : function(e, m){
34518         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34519             m.tryActivate(m.items.length-1, -1);
34520         }
34521     },
34522
34523     down : function(e, m){
34524         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34525             m.tryActivate(0, 1);
34526         }
34527     },
34528
34529     right : function(e, m){
34530         if(m.activeItem){
34531             m.activeItem.expandMenu(true);
34532         }
34533     },
34534
34535     left : function(e, m){
34536         m.hide();
34537         if(m.parentMenu && m.parentMenu.activeItem){
34538             m.parentMenu.activeItem.activate();
34539         }
34540     },
34541
34542     enter : function(e, m){
34543         if(m.activeItem){
34544             e.stopPropagation();
34545             m.activeItem.onClick(e);
34546             m.fireEvent("click", this, m.activeItem);
34547             return true;
34548         }
34549     }
34550 });/*
34551  * Based on:
34552  * Ext JS Library 1.1.1
34553  * Copyright(c) 2006-2007, Ext JS, LLC.
34554  *
34555  * Originally Released Under LGPL - original licence link has changed is not relivant.
34556  *
34557  * Fork - LGPL
34558  * <script type="text/javascript">
34559  */
34560  
34561 /**
34562  * @class Roo.menu.MenuMgr
34563  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34564  * @singleton
34565  */
34566 Roo.menu.MenuMgr = function(){
34567    var menus, active, groups = {}, attached = false, lastShow = new Date();
34568
34569    // private - called when first menu is created
34570    function init(){
34571        menus = {};
34572        active = new Roo.util.MixedCollection();
34573        Roo.get(document).addKeyListener(27, function(){
34574            if(active.length > 0){
34575                hideAll();
34576            }
34577        });
34578    }
34579
34580    // private
34581    function hideAll(){
34582        if(active && active.length > 0){
34583            var c = active.clone();
34584            c.each(function(m){
34585                m.hide();
34586            });
34587        }
34588    }
34589
34590    // private
34591    function onHide(m){
34592        active.remove(m);
34593        if(active.length < 1){
34594            Roo.get(document).un("mousedown", onMouseDown);
34595            attached = false;
34596        }
34597    }
34598
34599    // private
34600    function onShow(m){
34601        var last = active.last();
34602        lastShow = new Date();
34603        active.add(m);
34604        if(!attached){
34605            Roo.get(document).on("mousedown", onMouseDown);
34606            attached = true;
34607        }
34608        if(m.parentMenu){
34609           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34610           m.parentMenu.activeChild = m;
34611        }else if(last && last.isVisible()){
34612           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34613        }
34614    }
34615
34616    // private
34617    function onBeforeHide(m){
34618        if(m.activeChild){
34619            m.activeChild.hide();
34620        }
34621        if(m.autoHideTimer){
34622            clearTimeout(m.autoHideTimer);
34623            delete m.autoHideTimer;
34624        }
34625    }
34626
34627    // private
34628    function onBeforeShow(m){
34629        var pm = m.parentMenu;
34630        if(!pm && !m.allowOtherMenus){
34631            hideAll();
34632        }else if(pm && pm.activeChild && active != m){
34633            pm.activeChild.hide();
34634        }
34635    }
34636
34637    // private
34638    function onMouseDown(e){
34639        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34640            hideAll();
34641        }
34642    }
34643
34644    // private
34645    function onBeforeCheck(mi, state){
34646        if(state){
34647            var g = groups[mi.group];
34648            for(var i = 0, l = g.length; i < l; i++){
34649                if(g[i] != mi){
34650                    g[i].setChecked(false);
34651                }
34652            }
34653        }
34654    }
34655
34656    return {
34657
34658        /**
34659         * Hides all menus that are currently visible
34660         */
34661        hideAll : function(){
34662             hideAll();  
34663        },
34664
34665        // private
34666        register : function(menu){
34667            if(!menus){
34668                init();
34669            }
34670            menus[menu.id] = menu;
34671            menu.on("beforehide", onBeforeHide);
34672            menu.on("hide", onHide);
34673            menu.on("beforeshow", onBeforeShow);
34674            menu.on("show", onShow);
34675            var g = menu.group;
34676            if(g && menu.events["checkchange"]){
34677                if(!groups[g]){
34678                    groups[g] = [];
34679                }
34680                groups[g].push(menu);
34681                menu.on("checkchange", onCheck);
34682            }
34683        },
34684
34685         /**
34686          * Returns a {@link Roo.menu.Menu} object
34687          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34688          * be used to generate and return a new Menu instance.
34689          */
34690        get : function(menu){
34691            if(typeof menu == "string"){ // menu id
34692                return menus[menu];
34693            }else if(menu.events){  // menu instance
34694                return menu;
34695            }else if(typeof menu.length == 'number'){ // array of menu items?
34696                return new Roo.menu.Menu({items:menu});
34697            }else{ // otherwise, must be a config
34698                return new Roo.menu.Menu(menu);
34699            }
34700        },
34701
34702        // private
34703        unregister : function(menu){
34704            delete menus[menu.id];
34705            menu.un("beforehide", onBeforeHide);
34706            menu.un("hide", onHide);
34707            menu.un("beforeshow", onBeforeShow);
34708            menu.un("show", onShow);
34709            var g = menu.group;
34710            if(g && menu.events["checkchange"]){
34711                groups[g].remove(menu);
34712                menu.un("checkchange", onCheck);
34713            }
34714        },
34715
34716        // private
34717        registerCheckable : function(menuItem){
34718            var g = menuItem.group;
34719            if(g){
34720                if(!groups[g]){
34721                    groups[g] = [];
34722                }
34723                groups[g].push(menuItem);
34724                menuItem.on("beforecheckchange", onBeforeCheck);
34725            }
34726        },
34727
34728        // private
34729        unregisterCheckable : function(menuItem){
34730            var g = menuItem.group;
34731            if(g){
34732                groups[g].remove(menuItem);
34733                menuItem.un("beforecheckchange", onBeforeCheck);
34734            }
34735        }
34736    };
34737 }();/*
34738  * Based on:
34739  * Ext JS Library 1.1.1
34740  * Copyright(c) 2006-2007, Ext JS, LLC.
34741  *
34742  * Originally Released Under LGPL - original licence link has changed is not relivant.
34743  *
34744  * Fork - LGPL
34745  * <script type="text/javascript">
34746  */
34747  
34748
34749 /**
34750  * @class Roo.menu.BaseItem
34751  * @extends Roo.Component
34752  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34753  * management and base configuration options shared by all menu components.
34754  * @constructor
34755  * Creates a new BaseItem
34756  * @param {Object} config Configuration options
34757  */
34758 Roo.menu.BaseItem = function(config){
34759     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34760
34761     this.addEvents({
34762         /**
34763          * @event click
34764          * Fires when this item is clicked
34765          * @param {Roo.menu.BaseItem} this
34766          * @param {Roo.EventObject} e
34767          */
34768         click: true,
34769         /**
34770          * @event activate
34771          * Fires when this item is activated
34772          * @param {Roo.menu.BaseItem} this
34773          */
34774         activate : true,
34775         /**
34776          * @event deactivate
34777          * Fires when this item is deactivated
34778          * @param {Roo.menu.BaseItem} this
34779          */
34780         deactivate : true
34781     });
34782
34783     if(this.handler){
34784         this.on("click", this.handler, this.scope, true);
34785     }
34786 };
34787
34788 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34789     /**
34790      * @cfg {Function} handler
34791      * A function that will handle the click event of this menu item (defaults to undefined)
34792      */
34793     /**
34794      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34795      */
34796     canActivate : false,
34797     /**
34798      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34799      */
34800     activeClass : "x-menu-item-active",
34801     /**
34802      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34803      */
34804     hideOnClick : true,
34805     /**
34806      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34807      */
34808     hideDelay : 100,
34809
34810     // private
34811     ctype: "Roo.menu.BaseItem",
34812
34813     // private
34814     actionMode : "container",
34815
34816     // private
34817     render : function(container, parentMenu){
34818         this.parentMenu = parentMenu;
34819         Roo.menu.BaseItem.superclass.render.call(this, container);
34820         this.container.menuItemId = this.id;
34821     },
34822
34823     // private
34824     onRender : function(container, position){
34825         this.el = Roo.get(this.el);
34826         container.dom.appendChild(this.el.dom);
34827     },
34828
34829     // private
34830     onClick : function(e){
34831         if(!this.disabled && this.fireEvent("click", this, e) !== false
34832                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34833             this.handleClick(e);
34834         }else{
34835             e.stopEvent();
34836         }
34837     },
34838
34839     // private
34840     activate : function(){
34841         if(this.disabled){
34842             return false;
34843         }
34844         var li = this.container;
34845         li.addClass(this.activeClass);
34846         this.region = li.getRegion().adjust(2, 2, -2, -2);
34847         this.fireEvent("activate", this);
34848         return true;
34849     },
34850
34851     // private
34852     deactivate : function(){
34853         this.container.removeClass(this.activeClass);
34854         this.fireEvent("deactivate", this);
34855     },
34856
34857     // private
34858     shouldDeactivate : function(e){
34859         return !this.region || !this.region.contains(e.getPoint());
34860     },
34861
34862     // private
34863     handleClick : function(e){
34864         if(this.hideOnClick){
34865             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34866         }
34867     },
34868
34869     // private
34870     expandMenu : function(autoActivate){
34871         // do nothing
34872     },
34873
34874     // private
34875     hideMenu : function(){
34876         // do nothing
34877     }
34878 });/*
34879  * Based on:
34880  * Ext JS Library 1.1.1
34881  * Copyright(c) 2006-2007, Ext JS, LLC.
34882  *
34883  * Originally Released Under LGPL - original licence link has changed is not relivant.
34884  *
34885  * Fork - LGPL
34886  * <script type="text/javascript">
34887  */
34888  
34889 /**
34890  * @class Roo.menu.Adapter
34891  * @extends Roo.menu.BaseItem
34892  * 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.
34893  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34894  * @constructor
34895  * Creates a new Adapter
34896  * @param {Object} config Configuration options
34897  */
34898 Roo.menu.Adapter = function(component, config){
34899     Roo.menu.Adapter.superclass.constructor.call(this, config);
34900     this.component = component;
34901 };
34902 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34903     // private
34904     canActivate : true,
34905
34906     // private
34907     onRender : function(container, position){
34908         this.component.render(container);
34909         this.el = this.component.getEl();
34910     },
34911
34912     // private
34913     activate : function(){
34914         if(this.disabled){
34915             return false;
34916         }
34917         this.component.focus();
34918         this.fireEvent("activate", this);
34919         return true;
34920     },
34921
34922     // private
34923     deactivate : function(){
34924         this.fireEvent("deactivate", this);
34925     },
34926
34927     // private
34928     disable : function(){
34929         this.component.disable();
34930         Roo.menu.Adapter.superclass.disable.call(this);
34931     },
34932
34933     // private
34934     enable : function(){
34935         this.component.enable();
34936         Roo.menu.Adapter.superclass.enable.call(this);
34937     }
34938 });/*
34939  * Based on:
34940  * Ext JS Library 1.1.1
34941  * Copyright(c) 2006-2007, Ext JS, LLC.
34942  *
34943  * Originally Released Under LGPL - original licence link has changed is not relivant.
34944  *
34945  * Fork - LGPL
34946  * <script type="text/javascript">
34947  */
34948
34949 /**
34950  * @class Roo.menu.TextItem
34951  * @extends Roo.menu.BaseItem
34952  * Adds a static text string to a menu, usually used as either a heading or group separator.
34953  * Note: old style constructor with text is still supported.
34954  * 
34955  * @constructor
34956  * Creates a new TextItem
34957  * @param {Object} cfg Configuration
34958  */
34959 Roo.menu.TextItem = function(cfg){
34960     if (typeof(cfg) == 'string') {
34961         this.text = cfg;
34962     } else {
34963         Roo.apply(this,cfg);
34964     }
34965     
34966     Roo.menu.TextItem.superclass.constructor.call(this);
34967 };
34968
34969 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34970     /**
34971      * @cfg {Boolean} text Text to show on item.
34972      */
34973     text : '',
34974     
34975     /**
34976      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34977      */
34978     hideOnClick : false,
34979     /**
34980      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34981      */
34982     itemCls : "x-menu-text",
34983
34984     // private
34985     onRender : function(){
34986         var s = document.createElement("span");
34987         s.className = this.itemCls;
34988         s.innerHTML = this.text;
34989         this.el = s;
34990         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34991     }
34992 });/*
34993  * Based on:
34994  * Ext JS Library 1.1.1
34995  * Copyright(c) 2006-2007, Ext JS, LLC.
34996  *
34997  * Originally Released Under LGPL - original licence link has changed is not relivant.
34998  *
34999  * Fork - LGPL
35000  * <script type="text/javascript">
35001  */
35002
35003 /**
35004  * @class Roo.menu.Separator
35005  * @extends Roo.menu.BaseItem
35006  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35007  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35008  * @constructor
35009  * @param {Object} config Configuration options
35010  */
35011 Roo.menu.Separator = function(config){
35012     Roo.menu.Separator.superclass.constructor.call(this, config);
35013 };
35014
35015 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35016     /**
35017      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35018      */
35019     itemCls : "x-menu-sep",
35020     /**
35021      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35022      */
35023     hideOnClick : false,
35024
35025     // private
35026     onRender : function(li){
35027         var s = document.createElement("span");
35028         s.className = this.itemCls;
35029         s.innerHTML = "&#160;";
35030         this.el = s;
35031         li.addClass("x-menu-sep-li");
35032         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35033     }
35034 });/*
35035  * Based on:
35036  * Ext JS Library 1.1.1
35037  * Copyright(c) 2006-2007, Ext JS, LLC.
35038  *
35039  * Originally Released Under LGPL - original licence link has changed is not relivant.
35040  *
35041  * Fork - LGPL
35042  * <script type="text/javascript">
35043  */
35044 /**
35045  * @class Roo.menu.Item
35046  * @extends Roo.menu.BaseItem
35047  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35048  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35049  * activation and click handling.
35050  * @constructor
35051  * Creates a new Item
35052  * @param {Object} config Configuration options
35053  */
35054 Roo.menu.Item = function(config){
35055     Roo.menu.Item.superclass.constructor.call(this, config);
35056     if(this.menu){
35057         this.menu = Roo.menu.MenuMgr.get(this.menu);
35058     }
35059 };
35060 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35061     
35062     /**
35063      * @cfg {String} text
35064      * The text to show on the menu item.
35065      */
35066     text: '',
35067      /**
35068      * @cfg {String} HTML to render in menu
35069      * The text to show on the menu item (HTML version).
35070      */
35071     html: '',
35072     /**
35073      * @cfg {String} icon
35074      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35075      */
35076     icon: undefined,
35077     /**
35078      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35079      */
35080     itemCls : "x-menu-item",
35081     /**
35082      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35083      */
35084     canActivate : true,
35085     /**
35086      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35087      */
35088     showDelay: 200,
35089     // doc'd in BaseItem
35090     hideDelay: 200,
35091
35092     // private
35093     ctype: "Roo.menu.Item",
35094     
35095     // private
35096     onRender : function(container, position){
35097         var el = document.createElement("a");
35098         el.hideFocus = true;
35099         el.unselectable = "on";
35100         el.href = this.href || "#";
35101         if(this.hrefTarget){
35102             el.target = this.hrefTarget;
35103         }
35104         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35105         
35106         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35107         
35108         el.innerHTML = String.format(
35109                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35110                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35111         this.el = el;
35112         Roo.menu.Item.superclass.onRender.call(this, container, position);
35113     },
35114
35115     /**
35116      * Sets the text to display in this menu item
35117      * @param {String} text The text to display
35118      * @param {Boolean} isHTML true to indicate text is pure html.
35119      */
35120     setText : function(text, isHTML){
35121         if (isHTML) {
35122             this.html = text;
35123         } else {
35124             this.text = text;
35125             this.html = '';
35126         }
35127         if(this.rendered){
35128             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35129      
35130             this.el.update(String.format(
35131                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35132                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35133             this.parentMenu.autoWidth();
35134         }
35135     },
35136
35137     // private
35138     handleClick : function(e){
35139         if(!this.href){ // if no link defined, stop the event automatically
35140             e.stopEvent();
35141         }
35142         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35143     },
35144
35145     // private
35146     activate : function(autoExpand){
35147         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35148             this.focus();
35149             if(autoExpand){
35150                 this.expandMenu();
35151             }
35152         }
35153         return true;
35154     },
35155
35156     // private
35157     shouldDeactivate : function(e){
35158         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35159             if(this.menu && this.menu.isVisible()){
35160                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35161             }
35162             return true;
35163         }
35164         return false;
35165     },
35166
35167     // private
35168     deactivate : function(){
35169         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35170         this.hideMenu();
35171     },
35172
35173     // private
35174     expandMenu : function(autoActivate){
35175         if(!this.disabled && this.menu){
35176             clearTimeout(this.hideTimer);
35177             delete this.hideTimer;
35178             if(!this.menu.isVisible() && !this.showTimer){
35179                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35180             }else if (this.menu.isVisible() && autoActivate){
35181                 this.menu.tryActivate(0, 1);
35182             }
35183         }
35184     },
35185
35186     // private
35187     deferExpand : function(autoActivate){
35188         delete this.showTimer;
35189         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35190         if(autoActivate){
35191             this.menu.tryActivate(0, 1);
35192         }
35193     },
35194
35195     // private
35196     hideMenu : function(){
35197         clearTimeout(this.showTimer);
35198         delete this.showTimer;
35199         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35200             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35201         }
35202     },
35203
35204     // private
35205     deferHide : function(){
35206         delete this.hideTimer;
35207         this.menu.hide();
35208     }
35209 });/*
35210  * Based on:
35211  * Ext JS Library 1.1.1
35212  * Copyright(c) 2006-2007, Ext JS, LLC.
35213  *
35214  * Originally Released Under LGPL - original licence link has changed is not relivant.
35215  *
35216  * Fork - LGPL
35217  * <script type="text/javascript">
35218  */
35219  
35220 /**
35221  * @class Roo.menu.CheckItem
35222  * @extends Roo.menu.Item
35223  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35224  * @constructor
35225  * Creates a new CheckItem
35226  * @param {Object} config Configuration options
35227  */
35228 Roo.menu.CheckItem = function(config){
35229     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35230     this.addEvents({
35231         /**
35232          * @event beforecheckchange
35233          * Fires before the checked value is set, providing an opportunity to cancel if needed
35234          * @param {Roo.menu.CheckItem} this
35235          * @param {Boolean} checked The new checked value that will be set
35236          */
35237         "beforecheckchange" : true,
35238         /**
35239          * @event checkchange
35240          * Fires after the checked value has been set
35241          * @param {Roo.menu.CheckItem} this
35242          * @param {Boolean} checked The checked value that was set
35243          */
35244         "checkchange" : true
35245     });
35246     if(this.checkHandler){
35247         this.on('checkchange', this.checkHandler, this.scope);
35248     }
35249 };
35250 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35251     /**
35252      * @cfg {String} group
35253      * All check items with the same group name will automatically be grouped into a single-select
35254      * radio button group (defaults to '')
35255      */
35256     /**
35257      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35258      */
35259     itemCls : "x-menu-item x-menu-check-item",
35260     /**
35261      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35262      */
35263     groupClass : "x-menu-group-item",
35264
35265     /**
35266      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35267      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35268      * initialized with checked = true will be rendered as checked.
35269      */
35270     checked: false,
35271
35272     // private
35273     ctype: "Roo.menu.CheckItem",
35274
35275     // private
35276     onRender : function(c){
35277         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35278         if(this.group){
35279             this.el.addClass(this.groupClass);
35280         }
35281         Roo.menu.MenuMgr.registerCheckable(this);
35282         if(this.checked){
35283             this.checked = false;
35284             this.setChecked(true, true);
35285         }
35286     },
35287
35288     // private
35289     destroy : function(){
35290         if(this.rendered){
35291             Roo.menu.MenuMgr.unregisterCheckable(this);
35292         }
35293         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35294     },
35295
35296     /**
35297      * Set the checked state of this item
35298      * @param {Boolean} checked The new checked value
35299      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35300      */
35301     setChecked : function(state, suppressEvent){
35302         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35303             if(this.container){
35304                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35305             }
35306             this.checked = state;
35307             if(suppressEvent !== true){
35308                 this.fireEvent("checkchange", this, state);
35309             }
35310         }
35311     },
35312
35313     // private
35314     handleClick : function(e){
35315        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35316            this.setChecked(!this.checked);
35317        }
35318        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35319     }
35320 });/*
35321  * Based on:
35322  * Ext JS Library 1.1.1
35323  * Copyright(c) 2006-2007, Ext JS, LLC.
35324  *
35325  * Originally Released Under LGPL - original licence link has changed is not relivant.
35326  *
35327  * Fork - LGPL
35328  * <script type="text/javascript">
35329  */
35330  
35331 /**
35332  * @class Roo.menu.DateItem
35333  * @extends Roo.menu.Adapter
35334  * A menu item that wraps the {@link Roo.DatPicker} component.
35335  * @constructor
35336  * Creates a new DateItem
35337  * @param {Object} config Configuration options
35338  */
35339 Roo.menu.DateItem = function(config){
35340     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35341     /** The Roo.DatePicker object @type Roo.DatePicker */
35342     this.picker = this.component;
35343     this.addEvents({select: true});
35344     
35345     this.picker.on("render", function(picker){
35346         picker.getEl().swallowEvent("click");
35347         picker.container.addClass("x-menu-date-item");
35348     });
35349
35350     this.picker.on("select", this.onSelect, this);
35351 };
35352
35353 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35354     // private
35355     onSelect : function(picker, date){
35356         this.fireEvent("select", this, date, picker);
35357         Roo.menu.DateItem.superclass.handleClick.call(this);
35358     }
35359 });/*
35360  * Based on:
35361  * Ext JS Library 1.1.1
35362  * Copyright(c) 2006-2007, Ext JS, LLC.
35363  *
35364  * Originally Released Under LGPL - original licence link has changed is not relivant.
35365  *
35366  * Fork - LGPL
35367  * <script type="text/javascript">
35368  */
35369  
35370 /**
35371  * @class Roo.menu.ColorItem
35372  * @extends Roo.menu.Adapter
35373  * A menu item that wraps the {@link Roo.ColorPalette} component.
35374  * @constructor
35375  * Creates a new ColorItem
35376  * @param {Object} config Configuration options
35377  */
35378 Roo.menu.ColorItem = function(config){
35379     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35380     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35381     this.palette = this.component;
35382     this.relayEvents(this.palette, ["select"]);
35383     if(this.selectHandler){
35384         this.on('select', this.selectHandler, this.scope);
35385     }
35386 };
35387 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35388  * Based on:
35389  * Ext JS Library 1.1.1
35390  * Copyright(c) 2006-2007, Ext JS, LLC.
35391  *
35392  * Originally Released Under LGPL - original licence link has changed is not relivant.
35393  *
35394  * Fork - LGPL
35395  * <script type="text/javascript">
35396  */
35397  
35398
35399 /**
35400  * @class Roo.menu.DateMenu
35401  * @extends Roo.menu.Menu
35402  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35403  * @constructor
35404  * Creates a new DateMenu
35405  * @param {Object} config Configuration options
35406  */
35407 Roo.menu.DateMenu = function(config){
35408     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35409     this.plain = true;
35410     var di = new Roo.menu.DateItem(config);
35411     this.add(di);
35412     /**
35413      * The {@link Roo.DatePicker} instance for this DateMenu
35414      * @type DatePicker
35415      */
35416     this.picker = di.picker;
35417     /**
35418      * @event select
35419      * @param {DatePicker} picker
35420      * @param {Date} date
35421      */
35422     this.relayEvents(di, ["select"]);
35423
35424     this.on('beforeshow', function(){
35425         if(this.picker){
35426             this.picker.hideMonthPicker(true);
35427         }
35428     }, this);
35429 };
35430 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35431     cls:'x-date-menu'
35432 });/*
35433  * Based on:
35434  * Ext JS Library 1.1.1
35435  * Copyright(c) 2006-2007, Ext JS, LLC.
35436  *
35437  * Originally Released Under LGPL - original licence link has changed is not relivant.
35438  *
35439  * Fork - LGPL
35440  * <script type="text/javascript">
35441  */
35442  
35443
35444 /**
35445  * @class Roo.menu.ColorMenu
35446  * @extends Roo.menu.Menu
35447  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35448  * @constructor
35449  * Creates a new ColorMenu
35450  * @param {Object} config Configuration options
35451  */
35452 Roo.menu.ColorMenu = function(config){
35453     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35454     this.plain = true;
35455     var ci = new Roo.menu.ColorItem(config);
35456     this.add(ci);
35457     /**
35458      * The {@link Roo.ColorPalette} instance for this ColorMenu
35459      * @type ColorPalette
35460      */
35461     this.palette = ci.palette;
35462     /**
35463      * @event select
35464      * @param {ColorPalette} palette
35465      * @param {String} color
35466      */
35467     this.relayEvents(ci, ["select"]);
35468 };
35469 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35470  * Based on:
35471  * Ext JS Library 1.1.1
35472  * Copyright(c) 2006-2007, Ext JS, LLC.
35473  *
35474  * Originally Released Under LGPL - original licence link has changed is not relivant.
35475  *
35476  * Fork - LGPL
35477  * <script type="text/javascript">
35478  */
35479  
35480 /**
35481  * @class Roo.form.Field
35482  * @extends Roo.BoxComponent
35483  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35484  * @constructor
35485  * Creates a new Field
35486  * @param {Object} config Configuration options
35487  */
35488 Roo.form.Field = function(config){
35489     Roo.form.Field.superclass.constructor.call(this, config);
35490 };
35491
35492 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35493     /**
35494      * @cfg {String} fieldLabel Label to use when rendering a form.
35495      */
35496        /**
35497      * @cfg {String} qtip Mouse over tip
35498      */
35499      
35500     /**
35501      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35502      */
35503     invalidClass : "x-form-invalid",
35504     /**
35505      * @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")
35506      */
35507     invalidText : "The value in this field is invalid",
35508     /**
35509      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35510      */
35511     focusClass : "x-form-focus",
35512     /**
35513      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35514       automatic validation (defaults to "keyup").
35515      */
35516     validationEvent : "keyup",
35517     /**
35518      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35519      */
35520     validateOnBlur : true,
35521     /**
35522      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35523      */
35524     validationDelay : 250,
35525     /**
35526      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35527      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35528      */
35529     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35530     /**
35531      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35532      */
35533     fieldClass : "x-form-field",
35534     /**
35535      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35536      *<pre>
35537 Value         Description
35538 -----------   ----------------------------------------------------------------------
35539 qtip          Display a quick tip when the user hovers over the field
35540 title         Display a default browser title attribute popup
35541 under         Add a block div beneath the field containing the error text
35542 side          Add an error icon to the right of the field with a popup on hover
35543 [element id]  Add the error text directly to the innerHTML of the specified element
35544 </pre>
35545      */
35546     msgTarget : 'qtip',
35547     /**
35548      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35549      */
35550     msgFx : 'normal',
35551
35552     /**
35553      * @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.
35554      */
35555     readOnly : false,
35556
35557     /**
35558      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35559      */
35560     disabled : false,
35561
35562     /**
35563      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35564      */
35565     inputType : undefined,
35566     
35567     /**
35568      * @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).
35569          */
35570         tabIndex : undefined,
35571         
35572     // private
35573     isFormField : true,
35574
35575     // private
35576     hasFocus : false,
35577     /**
35578      * @property {Roo.Element} fieldEl
35579      * Element Containing the rendered Field (with label etc.)
35580      */
35581     /**
35582      * @cfg {Mixed} value A value to initialize this field with.
35583      */
35584     value : undefined,
35585
35586     /**
35587      * @cfg {String} name The field's HTML name attribute.
35588      */
35589     /**
35590      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35591      */
35592
35593         // private ??
35594         initComponent : function(){
35595         Roo.form.Field.superclass.initComponent.call(this);
35596         this.addEvents({
35597             /**
35598              * @event focus
35599              * Fires when this field receives input focus.
35600              * @param {Roo.form.Field} this
35601              */
35602             focus : true,
35603             /**
35604              * @event blur
35605              * Fires when this field loses input focus.
35606              * @param {Roo.form.Field} this
35607              */
35608             blur : true,
35609             /**
35610              * @event specialkey
35611              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35612              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35613              * @param {Roo.form.Field} this
35614              * @param {Roo.EventObject} e The event object
35615              */
35616             specialkey : true,
35617             /**
35618              * @event change
35619              * Fires just before the field blurs if the field value has changed.
35620              * @param {Roo.form.Field} this
35621              * @param {Mixed} newValue The new value
35622              * @param {Mixed} oldValue The original value
35623              */
35624             change : true,
35625             /**
35626              * @event invalid
35627              * Fires after the field has been marked as invalid.
35628              * @param {Roo.form.Field} this
35629              * @param {String} msg The validation message
35630              */
35631             invalid : true,
35632             /**
35633              * @event valid
35634              * Fires after the field has been validated with no errors.
35635              * @param {Roo.form.Field} this
35636              */
35637             valid : true,
35638              /**
35639              * @event keyup
35640              * Fires after the key up
35641              * @param {Roo.form.Field} this
35642              * @param {Roo.EventObject}  e The event Object
35643              */
35644             keyup : true
35645         });
35646     },
35647
35648     /**
35649      * Returns the name attribute of the field if available
35650      * @return {String} name The field name
35651      */
35652     getName: function(){
35653          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35654     },
35655
35656     // private
35657     onRender : function(ct, position){
35658         Roo.form.Field.superclass.onRender.call(this, ct, position);
35659         if(!this.el){
35660             var cfg = this.getAutoCreate();
35661             if(!cfg.name){
35662                 cfg.name = this.name || this.id;
35663             }
35664             if(this.inputType){
35665                 cfg.type = this.inputType;
35666             }
35667             this.el = ct.createChild(cfg, position);
35668         }
35669         var type = this.el.dom.type;
35670         if(type){
35671             if(type == 'password'){
35672                 type = 'text';
35673             }
35674             this.el.addClass('x-form-'+type);
35675         }
35676         if(this.readOnly){
35677             this.el.dom.readOnly = true;
35678         }
35679         if(this.tabIndex !== undefined){
35680             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35681         }
35682
35683         this.el.addClass([this.fieldClass, this.cls]);
35684         this.initValue();
35685     },
35686
35687     /**
35688      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35689      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35690      * @return {Roo.form.Field} this
35691      */
35692     applyTo : function(target){
35693         this.allowDomMove = false;
35694         this.el = Roo.get(target);
35695         this.render(this.el.dom.parentNode);
35696         return this;
35697     },
35698
35699     // private
35700     initValue : function(){
35701         if(this.value !== undefined){
35702             this.setValue(this.value);
35703         }else if(this.el.dom.value.length > 0){
35704             this.setValue(this.el.dom.value);
35705         }
35706     },
35707
35708     /**
35709      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35710      */
35711     isDirty : function() {
35712         if(this.disabled) {
35713             return false;
35714         }
35715         return String(this.getValue()) !== String(this.originalValue);
35716     },
35717
35718     // private
35719     afterRender : function(){
35720         Roo.form.Field.superclass.afterRender.call(this);
35721         this.initEvents();
35722     },
35723
35724     // private
35725     fireKey : function(e){
35726         //Roo.log('field ' + e.getKey());
35727         if(e.isNavKeyPress()){
35728             this.fireEvent("specialkey", this, e);
35729         }
35730     },
35731
35732     /**
35733      * Resets the current field value to the originally loaded value and clears any validation messages
35734      */
35735     reset : function(){
35736         this.setValue(this.originalValue);
35737         this.clearInvalid();
35738     },
35739
35740     // private
35741     initEvents : function(){
35742         // safari killled keypress - so keydown is now used..
35743         this.el.on("keydown" , this.fireKey,  this);
35744         this.el.on("focus", this.onFocus,  this);
35745         this.el.on("blur", this.onBlur,  this);
35746         this.el.relayEvent('keyup', this);
35747
35748         // reference to original value for reset
35749         this.originalValue = this.getValue();
35750     },
35751
35752     // private
35753     onFocus : function(){
35754         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35755             this.el.addClass(this.focusClass);
35756         }
35757         if(!this.hasFocus){
35758             this.hasFocus = true;
35759             this.startValue = this.getValue();
35760             this.fireEvent("focus", this);
35761         }
35762     },
35763
35764     beforeBlur : Roo.emptyFn,
35765
35766     // private
35767     onBlur : function(){
35768         this.beforeBlur();
35769         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35770             this.el.removeClass(this.focusClass);
35771         }
35772         this.hasFocus = false;
35773         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35774             this.validate();
35775         }
35776         var v = this.getValue();
35777         if(String(v) !== String(this.startValue)){
35778             this.fireEvent('change', this, v, this.startValue);
35779         }
35780         this.fireEvent("blur", this);
35781     },
35782
35783     /**
35784      * Returns whether or not the field value is currently valid
35785      * @param {Boolean} preventMark True to disable marking the field invalid
35786      * @return {Boolean} True if the value is valid, else false
35787      */
35788     isValid : function(preventMark){
35789         if(this.disabled){
35790             return true;
35791         }
35792         var restore = this.preventMark;
35793         this.preventMark = preventMark === true;
35794         var v = this.validateValue(this.processValue(this.getRawValue()));
35795         this.preventMark = restore;
35796         return v;
35797     },
35798
35799     /**
35800      * Validates the field value
35801      * @return {Boolean} True if the value is valid, else false
35802      */
35803     validate : function(){
35804         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35805             this.clearInvalid();
35806             return true;
35807         }
35808         return false;
35809     },
35810
35811     processValue : function(value){
35812         return value;
35813     },
35814
35815     // private
35816     // Subclasses should provide the validation implementation by overriding this
35817     validateValue : function(value){
35818         return true;
35819     },
35820
35821     /**
35822      * Mark this field as invalid
35823      * @param {String} msg The validation message
35824      */
35825     markInvalid : function(msg){
35826         if(!this.rendered || this.preventMark){ // not rendered
35827             return;
35828         }
35829         this.el.addClass(this.invalidClass);
35830         msg = msg || this.invalidText;
35831         switch(this.msgTarget){
35832             case 'qtip':
35833                 this.el.dom.qtip = msg;
35834                 this.el.dom.qclass = 'x-form-invalid-tip';
35835                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35836                     Roo.QuickTips.enable();
35837                 }
35838                 break;
35839             case 'title':
35840                 this.el.dom.title = msg;
35841                 break;
35842             case 'under':
35843                 if(!this.errorEl){
35844                     var elp = this.el.findParent('.x-form-element', 5, true);
35845                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35846                     this.errorEl.setWidth(elp.getWidth(true)-20);
35847                 }
35848                 this.errorEl.update(msg);
35849                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35850                 break;
35851             case 'side':
35852                 if(!this.errorIcon){
35853                     var elp = this.el.findParent('.x-form-element', 5, true);
35854                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35855                 }
35856                 this.alignErrorIcon();
35857                 this.errorIcon.dom.qtip = msg;
35858                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35859                 this.errorIcon.show();
35860                 this.on('resize', this.alignErrorIcon, this);
35861                 break;
35862             default:
35863                 var t = Roo.getDom(this.msgTarget);
35864                 t.innerHTML = msg;
35865                 t.style.display = this.msgDisplay;
35866                 break;
35867         }
35868         this.fireEvent('invalid', this, msg);
35869     },
35870
35871     // private
35872     alignErrorIcon : function(){
35873         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35874     },
35875
35876     /**
35877      * Clear any invalid styles/messages for this field
35878      */
35879     clearInvalid : function(){
35880         if(!this.rendered || this.preventMark){ // not rendered
35881             return;
35882         }
35883         this.el.removeClass(this.invalidClass);
35884         switch(this.msgTarget){
35885             case 'qtip':
35886                 this.el.dom.qtip = '';
35887                 break;
35888             case 'title':
35889                 this.el.dom.title = '';
35890                 break;
35891             case 'under':
35892                 if(this.errorEl){
35893                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35894                 }
35895                 break;
35896             case 'side':
35897                 if(this.errorIcon){
35898                     this.errorIcon.dom.qtip = '';
35899                     this.errorIcon.hide();
35900                     this.un('resize', this.alignErrorIcon, this);
35901                 }
35902                 break;
35903             default:
35904                 var t = Roo.getDom(this.msgTarget);
35905                 t.innerHTML = '';
35906                 t.style.display = 'none';
35907                 break;
35908         }
35909         this.fireEvent('valid', this);
35910     },
35911
35912     /**
35913      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35914      * @return {Mixed} value The field value
35915      */
35916     getRawValue : function(){
35917         var v = this.el.getValue();
35918         if(v === this.emptyText){
35919             v = '';
35920         }
35921         return v;
35922     },
35923
35924     /**
35925      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35926      * @return {Mixed} value The field value
35927      */
35928     getValue : function(){
35929         var v = this.el.getValue();
35930         if(v === this.emptyText || v === undefined){
35931             v = '';
35932         }
35933         return v;
35934     },
35935
35936     /**
35937      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35938      * @param {Mixed} value The value to set
35939      */
35940     setRawValue : function(v){
35941         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35942     },
35943
35944     /**
35945      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35946      * @param {Mixed} value The value to set
35947      */
35948     setValue : function(v){
35949         this.value = v;
35950         if(this.rendered){
35951             this.el.dom.value = (v === null || v === undefined ? '' : v);
35952              this.validate();
35953         }
35954     },
35955
35956     adjustSize : function(w, h){
35957         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35958         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35959         return s;
35960     },
35961
35962     adjustWidth : function(tag, w){
35963         tag = tag.toLowerCase();
35964         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35965             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35966                 if(tag == 'input'){
35967                     return w + 2;
35968                 }
35969                 if(tag = 'textarea'){
35970                     return w-2;
35971                 }
35972             }else if(Roo.isOpera){
35973                 if(tag == 'input'){
35974                     return w + 2;
35975                 }
35976                 if(tag = 'textarea'){
35977                     return w-2;
35978                 }
35979             }
35980         }
35981         return w;
35982     }
35983 });
35984
35985
35986 // anything other than normal should be considered experimental
35987 Roo.form.Field.msgFx = {
35988     normal : {
35989         show: function(msgEl, f){
35990             msgEl.setDisplayed('block');
35991         },
35992
35993         hide : function(msgEl, f){
35994             msgEl.setDisplayed(false).update('');
35995         }
35996     },
35997
35998     slide : {
35999         show: function(msgEl, f){
36000             msgEl.slideIn('t', {stopFx:true});
36001         },
36002
36003         hide : function(msgEl, f){
36004             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36005         }
36006     },
36007
36008     slideRight : {
36009         show: function(msgEl, f){
36010             msgEl.fixDisplay();
36011             msgEl.alignTo(f.el, 'tl-tr');
36012             msgEl.slideIn('l', {stopFx:true});
36013         },
36014
36015         hide : function(msgEl, f){
36016             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36017         }
36018     }
36019 };/*
36020  * Based on:
36021  * Ext JS Library 1.1.1
36022  * Copyright(c) 2006-2007, Ext JS, LLC.
36023  *
36024  * Originally Released Under LGPL - original licence link has changed is not relivant.
36025  *
36026  * Fork - LGPL
36027  * <script type="text/javascript">
36028  */
36029  
36030
36031 /**
36032  * @class Roo.form.TextField
36033  * @extends Roo.form.Field
36034  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36035  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36036  * @constructor
36037  * Creates a new TextField
36038  * @param {Object} config Configuration options
36039  */
36040 Roo.form.TextField = function(config){
36041     Roo.form.TextField.superclass.constructor.call(this, config);
36042     this.addEvents({
36043         /**
36044          * @event autosize
36045          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36046          * according to the default logic, but this event provides a hook for the developer to apply additional
36047          * logic at runtime to resize the field if needed.
36048              * @param {Roo.form.Field} this This text field
36049              * @param {Number} width The new field width
36050              */
36051         autosize : true
36052     });
36053 };
36054
36055 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36056     /**
36057      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36058      */
36059     grow : false,
36060     /**
36061      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36062      */
36063     growMin : 30,
36064     /**
36065      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36066      */
36067     growMax : 800,
36068     /**
36069      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36070      */
36071     vtype : null,
36072     /**
36073      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36074      */
36075     maskRe : null,
36076     /**
36077      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36078      */
36079     disableKeyFilter : false,
36080     /**
36081      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36082      */
36083     allowBlank : true,
36084     /**
36085      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36086      */
36087     minLength : 0,
36088     /**
36089      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36090      */
36091     maxLength : Number.MAX_VALUE,
36092     /**
36093      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36094      */
36095     minLengthText : "The minimum length for this field is {0}",
36096     /**
36097      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36098      */
36099     maxLengthText : "The maximum length for this field is {0}",
36100     /**
36101      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36102      */
36103     selectOnFocus : false,
36104     /**
36105      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36106      */
36107     blankText : "This field is required",
36108     /**
36109      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36110      * If available, this function will be called only after the basic validators all return true, and will be passed the
36111      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36112      */
36113     validator : null,
36114     /**
36115      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36116      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36117      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36118      */
36119     regex : null,
36120     /**
36121      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36122      */
36123     regexText : "",
36124     /**
36125      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36126      */
36127     emptyText : null,
36128     /**
36129      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36130      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36131      */
36132     emptyClass : 'x-form-empty-field',
36133
36134     // private
36135     initEvents : function(){
36136         Roo.form.TextField.superclass.initEvents.call(this);
36137         if(this.validationEvent == 'keyup'){
36138             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36139             this.el.on('keyup', this.filterValidation, this);
36140         }
36141         else if(this.validationEvent !== false){
36142             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36143         }
36144         if(this.selectOnFocus || this.emptyText){
36145             this.on("focus", this.preFocus, this);
36146             if(this.emptyText){
36147                 this.on('blur', this.postBlur, this);
36148                 this.applyEmptyText();
36149             }
36150         }
36151         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36152             this.el.on("keypress", this.filterKeys, this);
36153         }
36154         if(this.grow){
36155             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36156             this.el.on("click", this.autoSize,  this);
36157         }
36158     },
36159
36160     processValue : function(value){
36161         if(this.stripCharsRe){
36162             var newValue = value.replace(this.stripCharsRe, '');
36163             if(newValue !== value){
36164                 this.setRawValue(newValue);
36165                 return newValue;
36166             }
36167         }
36168         return value;
36169     },
36170
36171     filterValidation : function(e){
36172         if(!e.isNavKeyPress()){
36173             this.validationTask.delay(this.validationDelay);
36174         }
36175     },
36176
36177     // private
36178     onKeyUp : function(e){
36179         if(!e.isNavKeyPress()){
36180             this.autoSize();
36181         }
36182     },
36183
36184     /**
36185      * Resets the current field value to the originally-loaded value and clears any validation messages.
36186      * Also adds emptyText and emptyClass if the original value was blank.
36187      */
36188     reset : function(){
36189         Roo.form.TextField.superclass.reset.call(this);
36190         this.applyEmptyText();
36191     },
36192
36193     applyEmptyText : function(){
36194         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36195             this.setRawValue(this.emptyText);
36196             this.el.addClass(this.emptyClass);
36197         }
36198     },
36199
36200     // private
36201     preFocus : function(){
36202         if(this.emptyText){
36203             if(this.el.dom.value == this.emptyText){
36204                 this.setRawValue('');
36205             }
36206             this.el.removeClass(this.emptyClass);
36207         }
36208         if(this.selectOnFocus){
36209             this.el.dom.select();
36210         }
36211     },
36212
36213     // private
36214     postBlur : function(){
36215         this.applyEmptyText();
36216     },
36217
36218     // private
36219     filterKeys : function(e){
36220         var k = e.getKey();
36221         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36222             return;
36223         }
36224         var c = e.getCharCode(), cc = String.fromCharCode(c);
36225         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36226             return;
36227         }
36228         if(!this.maskRe.test(cc)){
36229             e.stopEvent();
36230         }
36231     },
36232
36233     setValue : function(v){
36234         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36235             this.el.removeClass(this.emptyClass);
36236         }
36237         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36238         this.applyEmptyText();
36239         this.autoSize();
36240     },
36241
36242     /**
36243      * Validates a value according to the field's validation rules and marks the field as invalid
36244      * if the validation fails
36245      * @param {Mixed} value The value to validate
36246      * @return {Boolean} True if the value is valid, else false
36247      */
36248     validateValue : function(value){
36249         if(value.length < 1 || value === this.emptyText){ // if it's blank
36250              if(this.allowBlank){
36251                 this.clearInvalid();
36252                 return true;
36253              }else{
36254                 this.markInvalid(this.blankText);
36255                 return false;
36256              }
36257         }
36258         if(value.length < this.minLength){
36259             this.markInvalid(String.format(this.minLengthText, this.minLength));
36260             return false;
36261         }
36262         if(value.length > this.maxLength){
36263             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36264             return false;
36265         }
36266         if(this.vtype){
36267             var vt = Roo.form.VTypes;
36268             if(!vt[this.vtype](value, this)){
36269                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36270                 return false;
36271             }
36272         }
36273         if(typeof this.validator == "function"){
36274             var msg = this.validator(value);
36275             if(msg !== true){
36276                 this.markInvalid(msg);
36277                 return false;
36278             }
36279         }
36280         if(this.regex && !this.regex.test(value)){
36281             this.markInvalid(this.regexText);
36282             return false;
36283         }
36284         return true;
36285     },
36286
36287     /**
36288      * Selects text in this field
36289      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36290      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36291      */
36292     selectText : function(start, end){
36293         var v = this.getRawValue();
36294         if(v.length > 0){
36295             start = start === undefined ? 0 : start;
36296             end = end === undefined ? v.length : end;
36297             var d = this.el.dom;
36298             if(d.setSelectionRange){
36299                 d.setSelectionRange(start, end);
36300             }else if(d.createTextRange){
36301                 var range = d.createTextRange();
36302                 range.moveStart("character", start);
36303                 range.moveEnd("character", v.length-end);
36304                 range.select();
36305             }
36306         }
36307     },
36308
36309     /**
36310      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36311      * This only takes effect if grow = true, and fires the autosize event.
36312      */
36313     autoSize : function(){
36314         if(!this.grow || !this.rendered){
36315             return;
36316         }
36317         if(!this.metrics){
36318             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36319         }
36320         var el = this.el;
36321         var v = el.dom.value;
36322         var d = document.createElement('div');
36323         d.appendChild(document.createTextNode(v));
36324         v = d.innerHTML;
36325         d = null;
36326         v += "&#160;";
36327         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36328         this.el.setWidth(w);
36329         this.fireEvent("autosize", this, w);
36330     }
36331 });/*
36332  * Based on:
36333  * Ext JS Library 1.1.1
36334  * Copyright(c) 2006-2007, Ext JS, LLC.
36335  *
36336  * Originally Released Under LGPL - original licence link has changed is not relivant.
36337  *
36338  * Fork - LGPL
36339  * <script type="text/javascript">
36340  */
36341  
36342 /**
36343  * @class Roo.form.Hidden
36344  * @extends Roo.form.TextField
36345  * Simple Hidden element used on forms 
36346  * 
36347  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36348  * 
36349  * @constructor
36350  * Creates a new Hidden form element.
36351  * @param {Object} config Configuration options
36352  */
36353
36354
36355
36356 // easy hidden field...
36357 Roo.form.Hidden = function(config){
36358     Roo.form.Hidden.superclass.constructor.call(this, config);
36359 };
36360   
36361 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36362     fieldLabel:      '',
36363     inputType:      'hidden',
36364     width:          50,
36365     allowBlank:     true,
36366     labelSeparator: '',
36367     hidden:         true,
36368     itemCls :       'x-form-item-display-none'
36369
36370
36371 });
36372
36373
36374 /*
36375  * Based on:
36376  * Ext JS Library 1.1.1
36377  * Copyright(c) 2006-2007, Ext JS, LLC.
36378  *
36379  * Originally Released Under LGPL - original licence link has changed is not relivant.
36380  *
36381  * Fork - LGPL
36382  * <script type="text/javascript">
36383  */
36384  
36385 /**
36386  * @class Roo.form.TriggerField
36387  * @extends Roo.form.TextField
36388  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36389  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36390  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36391  * for which you can provide a custom implementation.  For example:
36392  * <pre><code>
36393 var trigger = new Roo.form.TriggerField();
36394 trigger.onTriggerClick = myTriggerFn;
36395 trigger.applyTo('my-field');
36396 </code></pre>
36397  *
36398  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36399  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36400  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36401  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36402  * @constructor
36403  * Create a new TriggerField.
36404  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36405  * to the base TextField)
36406  */
36407 Roo.form.TriggerField = function(config){
36408     this.mimicing = false;
36409     Roo.form.TriggerField.superclass.constructor.call(this, config);
36410 };
36411
36412 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36413     /**
36414      * @cfg {String} triggerClass A CSS class to apply to the trigger
36415      */
36416     /**
36417      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36418      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36419      */
36420     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36421     /**
36422      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36423      */
36424     hideTrigger:false,
36425
36426     /** @cfg {Boolean} grow @hide */
36427     /** @cfg {Number} growMin @hide */
36428     /** @cfg {Number} growMax @hide */
36429
36430     /**
36431      * @hide 
36432      * @method
36433      */
36434     autoSize: Roo.emptyFn,
36435     // private
36436     monitorTab : true,
36437     // private
36438     deferHeight : true,
36439
36440     
36441     actionMode : 'wrap',
36442     // private
36443     onResize : function(w, h){
36444         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36445         if(typeof w == 'number'){
36446             var x = w - this.trigger.getWidth();
36447             this.el.setWidth(this.adjustWidth('input', x));
36448             this.trigger.setStyle('left', x+'px');
36449         }
36450     },
36451
36452     // private
36453     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36454
36455     // private
36456     getResizeEl : function(){
36457         return this.wrap;
36458     },
36459
36460     // private
36461     getPositionEl : function(){
36462         return this.wrap;
36463     },
36464
36465     // private
36466     alignErrorIcon : function(){
36467         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36468     },
36469
36470     // private
36471     onRender : function(ct, position){
36472         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36473         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36474         this.trigger = this.wrap.createChild(this.triggerConfig ||
36475                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36476         if(this.hideTrigger){
36477             this.trigger.setDisplayed(false);
36478         }
36479         this.initTrigger();
36480         if(!this.width){
36481             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36482         }
36483     },
36484
36485     // private
36486     initTrigger : function(){
36487         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36488         this.trigger.addClassOnOver('x-form-trigger-over');
36489         this.trigger.addClassOnClick('x-form-trigger-click');
36490     },
36491
36492     // private
36493     onDestroy : function(){
36494         if(this.trigger){
36495             this.trigger.removeAllListeners();
36496             this.trigger.remove();
36497         }
36498         if(this.wrap){
36499             this.wrap.remove();
36500         }
36501         Roo.form.TriggerField.superclass.onDestroy.call(this);
36502     },
36503
36504     // private
36505     onFocus : function(){
36506         Roo.form.TriggerField.superclass.onFocus.call(this);
36507         if(!this.mimicing){
36508             this.wrap.addClass('x-trigger-wrap-focus');
36509             this.mimicing = true;
36510             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36511             if(this.monitorTab){
36512                 this.el.on("keydown", this.checkTab, this);
36513             }
36514         }
36515     },
36516
36517     // private
36518     checkTab : function(e){
36519         if(e.getKey() == e.TAB){
36520             this.triggerBlur();
36521         }
36522     },
36523
36524     // private
36525     onBlur : function(){
36526         // do nothing
36527     },
36528
36529     // private
36530     mimicBlur : function(e, t){
36531         if(!this.wrap.contains(t) && this.validateBlur()){
36532             this.triggerBlur();
36533         }
36534     },
36535
36536     // private
36537     triggerBlur : function(){
36538         this.mimicing = false;
36539         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36540         if(this.monitorTab){
36541             this.el.un("keydown", this.checkTab, this);
36542         }
36543         this.wrap.removeClass('x-trigger-wrap-focus');
36544         Roo.form.TriggerField.superclass.onBlur.call(this);
36545     },
36546
36547     // private
36548     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36549     validateBlur : function(e, t){
36550         return true;
36551     },
36552
36553     // private
36554     onDisable : function(){
36555         Roo.form.TriggerField.superclass.onDisable.call(this);
36556         if(this.wrap){
36557             this.wrap.addClass('x-item-disabled');
36558         }
36559     },
36560
36561     // private
36562     onEnable : function(){
36563         Roo.form.TriggerField.superclass.onEnable.call(this);
36564         if(this.wrap){
36565             this.wrap.removeClass('x-item-disabled');
36566         }
36567     },
36568
36569     // private
36570     onShow : function(){
36571         var ae = this.getActionEl();
36572         
36573         if(ae){
36574             ae.dom.style.display = '';
36575             ae.dom.style.visibility = 'visible';
36576         }
36577     },
36578
36579     // private
36580     
36581     onHide : function(){
36582         var ae = this.getActionEl();
36583         ae.dom.style.display = 'none';
36584     },
36585
36586     /**
36587      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36588      * by an implementing function.
36589      * @method
36590      * @param {EventObject} e
36591      */
36592     onTriggerClick : Roo.emptyFn
36593 });
36594
36595 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36596 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36597 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36598 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36599     initComponent : function(){
36600         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36601
36602         this.triggerConfig = {
36603             tag:'span', cls:'x-form-twin-triggers', cn:[
36604             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36605             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36606         ]};
36607     },
36608
36609     getTrigger : function(index){
36610         return this.triggers[index];
36611     },
36612
36613     initTrigger : function(){
36614         var ts = this.trigger.select('.x-form-trigger', true);
36615         this.wrap.setStyle('overflow', 'hidden');
36616         var triggerField = this;
36617         ts.each(function(t, all, index){
36618             t.hide = function(){
36619                 var w = triggerField.wrap.getWidth();
36620                 this.dom.style.display = 'none';
36621                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36622             };
36623             t.show = function(){
36624                 var w = triggerField.wrap.getWidth();
36625                 this.dom.style.display = '';
36626                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36627             };
36628             var triggerIndex = 'Trigger'+(index+1);
36629
36630             if(this['hide'+triggerIndex]){
36631                 t.dom.style.display = 'none';
36632             }
36633             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36634             t.addClassOnOver('x-form-trigger-over');
36635             t.addClassOnClick('x-form-trigger-click');
36636         }, this);
36637         this.triggers = ts.elements;
36638     },
36639
36640     onTrigger1Click : Roo.emptyFn,
36641     onTrigger2Click : Roo.emptyFn
36642 });/*
36643  * Based on:
36644  * Ext JS Library 1.1.1
36645  * Copyright(c) 2006-2007, Ext JS, LLC.
36646  *
36647  * Originally Released Under LGPL - original licence link has changed is not relivant.
36648  *
36649  * Fork - LGPL
36650  * <script type="text/javascript">
36651  */
36652  
36653 /**
36654  * @class Roo.form.TextArea
36655  * @extends Roo.form.TextField
36656  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36657  * support for auto-sizing.
36658  * @constructor
36659  * Creates a new TextArea
36660  * @param {Object} config Configuration options
36661  */
36662 Roo.form.TextArea = function(config){
36663     Roo.form.TextArea.superclass.constructor.call(this, config);
36664     // these are provided exchanges for backwards compat
36665     // minHeight/maxHeight were replaced by growMin/growMax to be
36666     // compatible with TextField growing config values
36667     if(this.minHeight !== undefined){
36668         this.growMin = this.minHeight;
36669     }
36670     if(this.maxHeight !== undefined){
36671         this.growMax = this.maxHeight;
36672     }
36673 };
36674
36675 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36676     /**
36677      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36678      */
36679     growMin : 60,
36680     /**
36681      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36682      */
36683     growMax: 1000,
36684     /**
36685      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36686      * in the field (equivalent to setting overflow: hidden, defaults to false)
36687      */
36688     preventScrollbars: false,
36689     /**
36690      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36691      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36692      */
36693
36694     // private
36695     onRender : function(ct, position){
36696         if(!this.el){
36697             this.defaultAutoCreate = {
36698                 tag: "textarea",
36699                 style:"width:300px;height:60px;",
36700                 autocomplete: "off"
36701             };
36702         }
36703         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36704         if(this.grow){
36705             this.textSizeEl = Roo.DomHelper.append(document.body, {
36706                 tag: "pre", cls: "x-form-grow-sizer"
36707             });
36708             if(this.preventScrollbars){
36709                 this.el.setStyle("overflow", "hidden");
36710             }
36711             this.el.setHeight(this.growMin);
36712         }
36713     },
36714
36715     onDestroy : function(){
36716         if(this.textSizeEl){
36717             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36718         }
36719         Roo.form.TextArea.superclass.onDestroy.call(this);
36720     },
36721
36722     // private
36723     onKeyUp : function(e){
36724         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36725             this.autoSize();
36726         }
36727     },
36728
36729     /**
36730      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36731      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36732      */
36733     autoSize : function(){
36734         if(!this.grow || !this.textSizeEl){
36735             return;
36736         }
36737         var el = this.el;
36738         var v = el.dom.value;
36739         var ts = this.textSizeEl;
36740
36741         ts.innerHTML = '';
36742         ts.appendChild(document.createTextNode(v));
36743         v = ts.innerHTML;
36744
36745         Roo.fly(ts).setWidth(this.el.getWidth());
36746         if(v.length < 1){
36747             v = "&#160;&#160;";
36748         }else{
36749             if(Roo.isIE){
36750                 v = v.replace(/\n/g, '<p>&#160;</p>');
36751             }
36752             v += "&#160;\n&#160;";
36753         }
36754         ts.innerHTML = v;
36755         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36756         if(h != this.lastHeight){
36757             this.lastHeight = h;
36758             this.el.setHeight(h);
36759             this.fireEvent("autosize", this, h);
36760         }
36761     }
36762 });/*
36763  * Based on:
36764  * Ext JS Library 1.1.1
36765  * Copyright(c) 2006-2007, Ext JS, LLC.
36766  *
36767  * Originally Released Under LGPL - original licence link has changed is not relivant.
36768  *
36769  * Fork - LGPL
36770  * <script type="text/javascript">
36771  */
36772  
36773
36774 /**
36775  * @class Roo.form.NumberField
36776  * @extends Roo.form.TextField
36777  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36778  * @constructor
36779  * Creates a new NumberField
36780  * @param {Object} config Configuration options
36781  */
36782 Roo.form.NumberField = function(config){
36783     Roo.form.NumberField.superclass.constructor.call(this, config);
36784 };
36785
36786 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36787     /**
36788      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36789      */
36790     fieldClass: "x-form-field x-form-num-field",
36791     /**
36792      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36793      */
36794     allowDecimals : true,
36795     /**
36796      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36797      */
36798     decimalSeparator : ".",
36799     /**
36800      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36801      */
36802     decimalPrecision : 2,
36803     /**
36804      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36805      */
36806     allowNegative : true,
36807     /**
36808      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36809      */
36810     minValue : Number.NEGATIVE_INFINITY,
36811     /**
36812      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36813      */
36814     maxValue : Number.MAX_VALUE,
36815     /**
36816      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36817      */
36818     minText : "The minimum value for this field is {0}",
36819     /**
36820      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36821      */
36822     maxText : "The maximum value for this field is {0}",
36823     /**
36824      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36825      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36826      */
36827     nanText : "{0} is not a valid number",
36828
36829     // private
36830     initEvents : function(){
36831         Roo.form.NumberField.superclass.initEvents.call(this);
36832         var allowed = "0123456789";
36833         if(this.allowDecimals){
36834             allowed += this.decimalSeparator;
36835         }
36836         if(this.allowNegative){
36837             allowed += "-";
36838         }
36839         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36840         var keyPress = function(e){
36841             var k = e.getKey();
36842             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36843                 return;
36844             }
36845             var c = e.getCharCode();
36846             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36847                 e.stopEvent();
36848             }
36849         };
36850         this.el.on("keypress", keyPress, this);
36851     },
36852
36853     // private
36854     validateValue : function(value){
36855         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36856             return false;
36857         }
36858         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36859              return true;
36860         }
36861         var num = this.parseValue(value);
36862         if(isNaN(num)){
36863             this.markInvalid(String.format(this.nanText, value));
36864             return false;
36865         }
36866         if(num < this.minValue){
36867             this.markInvalid(String.format(this.minText, this.minValue));
36868             return false;
36869         }
36870         if(num > this.maxValue){
36871             this.markInvalid(String.format(this.maxText, this.maxValue));
36872             return false;
36873         }
36874         return true;
36875     },
36876
36877     getValue : function(){
36878         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36879     },
36880
36881     // private
36882     parseValue : function(value){
36883         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36884         return isNaN(value) ? '' : value;
36885     },
36886
36887     // private
36888     fixPrecision : function(value){
36889         var nan = isNaN(value);
36890         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36891             return nan ? '' : value;
36892         }
36893         return parseFloat(value).toFixed(this.decimalPrecision);
36894     },
36895
36896     setValue : function(v){
36897         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36898     },
36899
36900     // private
36901     decimalPrecisionFcn : function(v){
36902         return Math.floor(v);
36903     },
36904
36905     beforeBlur : function(){
36906         var v = this.parseValue(this.getRawValue());
36907         if(v){
36908             this.setValue(this.fixPrecision(v));
36909         }
36910     }
36911 });/*
36912  * Based on:
36913  * Ext JS Library 1.1.1
36914  * Copyright(c) 2006-2007, Ext JS, LLC.
36915  *
36916  * Originally Released Under LGPL - original licence link has changed is not relivant.
36917  *
36918  * Fork - LGPL
36919  * <script type="text/javascript">
36920  */
36921  
36922 /**
36923  * @class Roo.form.DateField
36924  * @extends Roo.form.TriggerField
36925  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36926 * @constructor
36927 * Create a new DateField
36928 * @param {Object} config
36929  */
36930 Roo.form.DateField = function(config){
36931     Roo.form.DateField.superclass.constructor.call(this, config);
36932     
36933       this.addEvents({
36934          
36935         /**
36936          * @event select
36937          * Fires when a date is selected
36938              * @param {Roo.form.DateField} combo This combo box
36939              * @param {Date} date The date selected
36940              */
36941         'select' : true
36942          
36943     });
36944     
36945     
36946     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36947     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36948     this.ddMatch = null;
36949     if(this.disabledDates){
36950         var dd = this.disabledDates;
36951         var re = "(?:";
36952         for(var i = 0; i < dd.length; i++){
36953             re += dd[i];
36954             if(i != dd.length-1) re += "|";
36955         }
36956         this.ddMatch = new RegExp(re + ")");
36957     }
36958 };
36959
36960 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36961     /**
36962      * @cfg {String} format
36963      * The default date format string which can be overriden for localization support.  The format must be
36964      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36965      */
36966     format : "m/d/y",
36967     /**
36968      * @cfg {String} altFormats
36969      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36970      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36971      */
36972     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36973     /**
36974      * @cfg {Array} disabledDays
36975      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36976      */
36977     disabledDays : null,
36978     /**
36979      * @cfg {String} disabledDaysText
36980      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36981      */
36982     disabledDaysText : "Disabled",
36983     /**
36984      * @cfg {Array} disabledDates
36985      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36986      * expression so they are very powerful. Some examples:
36987      * <ul>
36988      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36989      * <li>["03/08", "09/16"] would disable those days for every year</li>
36990      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36991      * <li>["03/../2006"] would disable every day in March 2006</li>
36992      * <li>["^03"] would disable every day in every March</li>
36993      * </ul>
36994      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36995      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36996      */
36997     disabledDates : null,
36998     /**
36999      * @cfg {String} disabledDatesText
37000      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37001      */
37002     disabledDatesText : "Disabled",
37003     /**
37004      * @cfg {Date/String} minValue
37005      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37006      * valid format (defaults to null).
37007      */
37008     minValue : null,
37009     /**
37010      * @cfg {Date/String} maxValue
37011      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37012      * valid format (defaults to null).
37013      */
37014     maxValue : null,
37015     /**
37016      * @cfg {String} minText
37017      * The error text to display when the date in the cell is before minValue (defaults to
37018      * 'The date in this field must be after {minValue}').
37019      */
37020     minText : "The date in this field must be equal to or after {0}",
37021     /**
37022      * @cfg {String} maxText
37023      * The error text to display when the date in the cell is after maxValue (defaults to
37024      * 'The date in this field must be before {maxValue}').
37025      */
37026     maxText : "The date in this field must be equal to or before {0}",
37027     /**
37028      * @cfg {String} invalidText
37029      * The error text to display when the date in the field is invalid (defaults to
37030      * '{value} is not a valid date - it must be in the format {format}').
37031      */
37032     invalidText : "{0} is not a valid date - it must be in the format {1}",
37033     /**
37034      * @cfg {String} triggerClass
37035      * An additional CSS class used to style the trigger button.  The trigger will always get the
37036      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37037      * which displays a calendar icon).
37038      */
37039     triggerClass : 'x-form-date-trigger',
37040     
37041
37042     /**
37043      * @cfg {bool} useIso
37044      * if enabled, then the date field will use a hidden field to store the 
37045      * real value as iso formated date. default (false)
37046      */ 
37047     useIso : false,
37048     /**
37049      * @cfg {String/Object} autoCreate
37050      * A DomHelper element spec, or true for a default element spec (defaults to
37051      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37052      */ 
37053     // private
37054     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37055     
37056     // private
37057     hiddenField: false,
37058     
37059     onRender : function(ct, position)
37060     {
37061         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37062         if (this.useIso) {
37063             this.el.dom.removeAttribute('name'); 
37064             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37065                     'before', true);
37066             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37067             // prevent input submission
37068             this.hiddenName = this.name;
37069         }
37070             
37071             
37072     },
37073     
37074     // private
37075     validateValue : function(value)
37076     {
37077         value = this.formatDate(value);
37078         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37079             return false;
37080         }
37081         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37082              return true;
37083         }
37084         var svalue = value;
37085         value = this.parseDate(value);
37086         if(!value){
37087             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37088             return false;
37089         }
37090         var time = value.getTime();
37091         if(this.minValue && time < this.minValue.getTime()){
37092             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37093             return false;
37094         }
37095         if(this.maxValue && time > this.maxValue.getTime()){
37096             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37097             return false;
37098         }
37099         if(this.disabledDays){
37100             var day = value.getDay();
37101             for(var i = 0; i < this.disabledDays.length; i++) {
37102                 if(day === this.disabledDays[i]){
37103                     this.markInvalid(this.disabledDaysText);
37104                     return false;
37105                 }
37106             }
37107         }
37108         var fvalue = this.formatDate(value);
37109         if(this.ddMatch && this.ddMatch.test(fvalue)){
37110             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37111             return false;
37112         }
37113         return true;
37114     },
37115
37116     // private
37117     // Provides logic to override the default TriggerField.validateBlur which just returns true
37118     validateBlur : function(){
37119         return !this.menu || !this.menu.isVisible();
37120     },
37121
37122     /**
37123      * Returns the current date value of the date field.
37124      * @return {Date} The date value
37125      */
37126     getValue : function(){
37127         
37128         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37129     },
37130
37131     /**
37132      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37133      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37134      * (the default format used is "m/d/y").
37135      * <br />Usage:
37136      * <pre><code>
37137 //All of these calls set the same date value (May 4, 2006)
37138
37139 //Pass a date object:
37140 var dt = new Date('5/4/06');
37141 dateField.setValue(dt);
37142
37143 //Pass a date string (default format):
37144 dateField.setValue('5/4/06');
37145
37146 //Pass a date string (custom format):
37147 dateField.format = 'Y-m-d';
37148 dateField.setValue('2006-5-4');
37149 </code></pre>
37150      * @param {String/Date} date The date or valid date string
37151      */
37152     setValue : function(date){
37153         if (this.hiddenField) {
37154             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37155         }
37156         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37157     },
37158
37159     // private
37160     parseDate : function(value){
37161         if(!value || value instanceof Date){
37162             return value;
37163         }
37164         var v = Date.parseDate(value, this.format);
37165         if(!v && this.altFormats){
37166             if(!this.altFormatsArray){
37167                 this.altFormatsArray = this.altFormats.split("|");
37168             }
37169             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37170                 v = Date.parseDate(value, this.altFormatsArray[i]);
37171             }
37172         }
37173         return v;
37174     },
37175
37176     // private
37177     formatDate : function(date, fmt){
37178         return (!date || !(date instanceof Date)) ?
37179                date : date.dateFormat(fmt || this.format);
37180     },
37181
37182     // private
37183     menuListeners : {
37184         select: function(m, d){
37185             this.setValue(d);
37186             this.fireEvent('select', this, d);
37187         },
37188         show : function(){ // retain focus styling
37189             this.onFocus();
37190         },
37191         hide : function(){
37192             this.focus.defer(10, this);
37193             var ml = this.menuListeners;
37194             this.menu.un("select", ml.select,  this);
37195             this.menu.un("show", ml.show,  this);
37196             this.menu.un("hide", ml.hide,  this);
37197         }
37198     },
37199
37200     // private
37201     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37202     onTriggerClick : function(){
37203         if(this.disabled){
37204             return;
37205         }
37206         if(this.menu == null){
37207             this.menu = new Roo.menu.DateMenu();
37208         }
37209         Roo.apply(this.menu.picker,  {
37210             showClear: this.allowBlank,
37211             minDate : this.minValue,
37212             maxDate : this.maxValue,
37213             disabledDatesRE : this.ddMatch,
37214             disabledDatesText : this.disabledDatesText,
37215             disabledDays : this.disabledDays,
37216             disabledDaysText : this.disabledDaysText,
37217             format : this.format,
37218             minText : String.format(this.minText, this.formatDate(this.minValue)),
37219             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37220         });
37221         this.menu.on(Roo.apply({}, this.menuListeners, {
37222             scope:this
37223         }));
37224         this.menu.picker.setValue(this.getValue() || new Date());
37225         this.menu.show(this.el, "tl-bl?");
37226     },
37227
37228     beforeBlur : function(){
37229         var v = this.parseDate(this.getRawValue());
37230         if(v){
37231             this.setValue(v);
37232         }
37233     }
37234
37235     /** @cfg {Boolean} grow @hide */
37236     /** @cfg {Number} growMin @hide */
37237     /** @cfg {Number} growMax @hide */
37238     /**
37239      * @hide
37240      * @method autoSize
37241      */
37242 });/*
37243  * Based on:
37244  * Ext JS Library 1.1.1
37245  * Copyright(c) 2006-2007, Ext JS, LLC.
37246  *
37247  * Originally Released Under LGPL - original licence link has changed is not relivant.
37248  *
37249  * Fork - LGPL
37250  * <script type="text/javascript">
37251  */
37252  
37253
37254 /**
37255  * @class Roo.form.ComboBox
37256  * @extends Roo.form.TriggerField
37257  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37258  * @constructor
37259  * Create a new ComboBox.
37260  * @param {Object} config Configuration options
37261  */
37262 Roo.form.ComboBox = function(config){
37263     Roo.form.ComboBox.superclass.constructor.call(this, config);
37264     this.addEvents({
37265         /**
37266          * @event expand
37267          * Fires when the dropdown list is expanded
37268              * @param {Roo.form.ComboBox} combo This combo box
37269              */
37270         'expand' : true,
37271         /**
37272          * @event collapse
37273          * Fires when the dropdown list is collapsed
37274              * @param {Roo.form.ComboBox} combo This combo box
37275              */
37276         'collapse' : true,
37277         /**
37278          * @event beforeselect
37279          * Fires before a list item is selected. Return false to cancel the selection.
37280              * @param {Roo.form.ComboBox} combo This combo box
37281              * @param {Roo.data.Record} record The data record returned from the underlying store
37282              * @param {Number} index The index of the selected item in the dropdown list
37283              */
37284         'beforeselect' : true,
37285         /**
37286          * @event select
37287          * Fires when a list item is selected
37288              * @param {Roo.form.ComboBox} combo This combo box
37289              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37290              * @param {Number} index The index of the selected item in the dropdown list
37291              */
37292         'select' : true,
37293         /**
37294          * @event beforequery
37295          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37296          * The event object passed has these properties:
37297              * @param {Roo.form.ComboBox} combo This combo box
37298              * @param {String} query The query
37299              * @param {Boolean} forceAll true to force "all" query
37300              * @param {Boolean} cancel true to cancel the query
37301              * @param {Object} e The query event object
37302              */
37303         'beforequery': true,
37304          /**
37305          * @event add
37306          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37307              * @param {Roo.form.ComboBox} combo This combo box
37308              */
37309         'add' : true,
37310         /**
37311          * @event edit
37312          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37313              * @param {Roo.form.ComboBox} combo This combo box
37314              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37315              */
37316         'edit' : true
37317         
37318         
37319     });
37320     if(this.transform){
37321         this.allowDomMove = false;
37322         var s = Roo.getDom(this.transform);
37323         if(!this.hiddenName){
37324             this.hiddenName = s.name;
37325         }
37326         if(!this.store){
37327             this.mode = 'local';
37328             var d = [], opts = s.options;
37329             for(var i = 0, len = opts.length;i < len; i++){
37330                 var o = opts[i];
37331                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37332                 if(o.selected) {
37333                     this.value = value;
37334                 }
37335                 d.push([value, o.text]);
37336             }
37337             this.store = new Roo.data.SimpleStore({
37338                 'id': 0,
37339                 fields: ['value', 'text'],
37340                 data : d
37341             });
37342             this.valueField = 'value';
37343             this.displayField = 'text';
37344         }
37345         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37346         if(!this.lazyRender){
37347             this.target = true;
37348             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37349             s.parentNode.removeChild(s); // remove it
37350             this.render(this.el.parentNode);
37351         }else{
37352             s.parentNode.removeChild(s); // remove it
37353         }
37354
37355     }
37356     if (this.store) {
37357         this.store = Roo.factory(this.store, Roo.data);
37358     }
37359     
37360     this.selectedIndex = -1;
37361     if(this.mode == 'local'){
37362         if(config.queryDelay === undefined){
37363             this.queryDelay = 10;
37364         }
37365         if(config.minChars === undefined){
37366             this.minChars = 0;
37367         }
37368     }
37369 };
37370
37371 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37372     /**
37373      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37374      */
37375     /**
37376      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37377      * rendering into an Roo.Editor, defaults to false)
37378      */
37379     /**
37380      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37381      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37382      */
37383     /**
37384      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37385      */
37386     /**
37387      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37388      * the dropdown list (defaults to undefined, with no header element)
37389      */
37390
37391      /**
37392      * @cfg {String/Roo.Template} tpl The template to use to render the output
37393      */
37394      
37395     // private
37396     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37397     /**
37398      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37399      */
37400     listWidth: undefined,
37401     /**
37402      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37403      * mode = 'remote' or 'text' if mode = 'local')
37404      */
37405     displayField: undefined,
37406     /**
37407      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37408      * mode = 'remote' or 'value' if mode = 'local'). 
37409      * Note: use of a valueField requires the user make a selection
37410      * in order for a value to be mapped.
37411      */
37412     valueField: undefined,
37413     
37414     
37415     /**
37416      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37417      * field's data value (defaults to the underlying DOM element's name)
37418      */
37419     hiddenName: undefined,
37420     /**
37421      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37422      */
37423     listClass: '',
37424     /**
37425      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37426      */
37427     selectedClass: 'x-combo-selected',
37428     /**
37429      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37430      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37431      * which displays a downward arrow icon).
37432      */
37433     triggerClass : 'x-form-arrow-trigger',
37434     /**
37435      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37436      */
37437     shadow:'sides',
37438     /**
37439      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37440      * anchor positions (defaults to 'tl-bl')
37441      */
37442     listAlign: 'tl-bl?',
37443     /**
37444      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37445      */
37446     maxHeight: 300,
37447     /**
37448      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37449      * query specified by the allQuery config option (defaults to 'query')
37450      */
37451     triggerAction: 'query',
37452     /**
37453      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37454      * (defaults to 4, does not apply if editable = false)
37455      */
37456     minChars : 4,
37457     /**
37458      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37459      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37460      */
37461     typeAhead: false,
37462     /**
37463      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37464      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37465      */
37466     queryDelay: 500,
37467     /**
37468      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37469      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37470      */
37471     pageSize: 0,
37472     /**
37473      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37474      * when editable = true (defaults to false)
37475      */
37476     selectOnFocus:false,
37477     /**
37478      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37479      */
37480     queryParam: 'query',
37481     /**
37482      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37483      * when mode = 'remote' (defaults to 'Loading...')
37484      */
37485     loadingText: 'Loading...',
37486     /**
37487      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37488      */
37489     resizable: false,
37490     /**
37491      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37492      */
37493     handleHeight : 8,
37494     /**
37495      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37496      * traditional select (defaults to true)
37497      */
37498     editable: true,
37499     /**
37500      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37501      */
37502     allQuery: '',
37503     /**
37504      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37505      */
37506     mode: 'remote',
37507     /**
37508      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37509      * listWidth has a higher value)
37510      */
37511     minListWidth : 70,
37512     /**
37513      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37514      * allow the user to set arbitrary text into the field (defaults to false)
37515      */
37516     forceSelection:false,
37517     /**
37518      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37519      * if typeAhead = true (defaults to 250)
37520      */
37521     typeAheadDelay : 250,
37522     /**
37523      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37524      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37525      */
37526     valueNotFoundText : undefined,
37527     /**
37528      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37529      */
37530     blockFocus : false,
37531     
37532     /**
37533      * @cfg {Boolean} disableClear Disable showing of clear button.
37534      */
37535     disableClear : false,
37536     /**
37537      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37538      */
37539     alwaysQuery : false,
37540     
37541     //private
37542     addicon : false,
37543     editicon: false,
37544     
37545     // element that contains real text value.. (when hidden is used..)
37546      
37547     // private
37548     onRender : function(ct, position){
37549         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37550         if(this.hiddenName){
37551             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37552                     'before', true);
37553             this.hiddenField.value =
37554                 this.hiddenValue !== undefined ? this.hiddenValue :
37555                 this.value !== undefined ? this.value : '';
37556
37557             // prevent input submission
37558             this.el.dom.removeAttribute('name');
37559              
37560              
37561         }
37562         if(Roo.isGecko){
37563             this.el.dom.setAttribute('autocomplete', 'off');
37564         }
37565
37566         var cls = 'x-combo-list';
37567
37568         this.list = new Roo.Layer({
37569             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37570         });
37571
37572         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37573         this.list.setWidth(lw);
37574         this.list.swallowEvent('mousewheel');
37575         this.assetHeight = 0;
37576
37577         if(this.title){
37578             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37579             this.assetHeight += this.header.getHeight();
37580         }
37581
37582         this.innerList = this.list.createChild({cls:cls+'-inner'});
37583         this.innerList.on('mouseover', this.onViewOver, this);
37584         this.innerList.on('mousemove', this.onViewMove, this);
37585         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37586         
37587         if(this.allowBlank && !this.pageSize && !this.disableClear){
37588             this.footer = this.list.createChild({cls:cls+'-ft'});
37589             this.pageTb = new Roo.Toolbar(this.footer);
37590            
37591         }
37592         if(this.pageSize){
37593             this.footer = this.list.createChild({cls:cls+'-ft'});
37594             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37595                     {pageSize: this.pageSize});
37596             
37597         }
37598         
37599         if (this.pageTb && this.allowBlank && !this.disableClear) {
37600             var _this = this;
37601             this.pageTb.add(new Roo.Toolbar.Fill(), {
37602                 cls: 'x-btn-icon x-btn-clear',
37603                 text: '&#160;',
37604                 handler: function()
37605                 {
37606                     _this.collapse();
37607                     _this.clearValue();
37608                     _this.onSelect(false, -1);
37609                 }
37610             });
37611         }
37612         if (this.footer) {
37613             this.assetHeight += this.footer.getHeight();
37614         }
37615         
37616
37617         if(!this.tpl){
37618             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37619         }
37620
37621         this.view = new Roo.View(this.innerList, this.tpl, {
37622             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37623         });
37624
37625         this.view.on('click', this.onViewClick, this);
37626
37627         this.store.on('beforeload', this.onBeforeLoad, this);
37628         this.store.on('load', this.onLoad, this);
37629         this.store.on('loadexception', this.onLoadException, this);
37630
37631         if(this.resizable){
37632             this.resizer = new Roo.Resizable(this.list,  {
37633                pinned:true, handles:'se'
37634             });
37635             this.resizer.on('resize', function(r, w, h){
37636                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37637                 this.listWidth = w;
37638                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37639                 this.restrictHeight();
37640             }, this);
37641             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37642         }
37643         if(!this.editable){
37644             this.editable = true;
37645             this.setEditable(false);
37646         }  
37647         
37648         
37649         if (typeof(this.events.add.listeners) != 'undefined') {
37650             
37651             this.addicon = this.wrap.createChild(
37652                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37653        
37654             this.addicon.on('click', function(e) {
37655                 this.fireEvent('add', this);
37656             }, this);
37657         }
37658         if (typeof(this.events.edit.listeners) != 'undefined') {
37659             
37660             this.editicon = this.wrap.createChild(
37661                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37662             if (this.addicon) {
37663                 this.editicon.setStyle('margin-left', '40px');
37664             }
37665             this.editicon.on('click', function(e) {
37666                 
37667                 // we fire even  if inothing is selected..
37668                 this.fireEvent('edit', this, this.lastData );
37669                 
37670             }, this);
37671         }
37672         
37673         
37674         
37675     },
37676
37677     // private
37678     initEvents : function(){
37679         Roo.form.ComboBox.superclass.initEvents.call(this);
37680
37681         this.keyNav = new Roo.KeyNav(this.el, {
37682             "up" : function(e){
37683                 this.inKeyMode = true;
37684                 this.selectPrev();
37685             },
37686
37687             "down" : function(e){
37688                 if(!this.isExpanded()){
37689                     this.onTriggerClick();
37690                 }else{
37691                     this.inKeyMode = true;
37692                     this.selectNext();
37693                 }
37694             },
37695
37696             "enter" : function(e){
37697                 this.onViewClick();
37698                 //return true;
37699             },
37700
37701             "esc" : function(e){
37702                 this.collapse();
37703             },
37704
37705             "tab" : function(e){
37706                 this.onViewClick(false);
37707                 this.fireEvent("specialkey", this, e);
37708                 return true;
37709             },
37710
37711             scope : this,
37712
37713             doRelay : function(foo, bar, hname){
37714                 if(hname == 'down' || this.scope.isExpanded()){
37715                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37716                 }
37717                 return true;
37718             },
37719
37720             forceKeyDown: true
37721         });
37722         this.queryDelay = Math.max(this.queryDelay || 10,
37723                 this.mode == 'local' ? 10 : 250);
37724         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37725         if(this.typeAhead){
37726             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37727         }
37728         if(this.editable !== false){
37729             this.el.on("keyup", this.onKeyUp, this);
37730         }
37731         if(this.forceSelection){
37732             this.on('blur', this.doForce, this);
37733         }
37734     },
37735
37736     onDestroy : function(){
37737         if(this.view){
37738             this.view.setStore(null);
37739             this.view.el.removeAllListeners();
37740             this.view.el.remove();
37741             this.view.purgeListeners();
37742         }
37743         if(this.list){
37744             this.list.destroy();
37745         }
37746         if(this.store){
37747             this.store.un('beforeload', this.onBeforeLoad, this);
37748             this.store.un('load', this.onLoad, this);
37749             this.store.un('loadexception', this.onLoadException, this);
37750         }
37751         Roo.form.ComboBox.superclass.onDestroy.call(this);
37752     },
37753
37754     // private
37755     fireKey : function(e){
37756         if(e.isNavKeyPress() && !this.list.isVisible()){
37757             this.fireEvent("specialkey", this, e);
37758         }
37759     },
37760
37761     // private
37762     onResize: function(w, h){
37763         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37764         
37765         if(typeof w != 'number'){
37766             // we do not handle it!?!?
37767             return;
37768         }
37769         var tw = this.trigger.getWidth();
37770         tw += this.addicon ? this.addicon.getWidth() : 0;
37771         tw += this.editicon ? this.editicon.getWidth() : 0;
37772         var x = w - tw;
37773         this.el.setWidth( this.adjustWidth('input', x));
37774             
37775         this.trigger.setStyle('left', x+'px');
37776         
37777         if(this.list && this.listWidth === undefined){
37778             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37779             this.list.setWidth(lw);
37780             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37781         }
37782         
37783     
37784         
37785     },
37786
37787     /**
37788      * Allow or prevent the user from directly editing the field text.  If false is passed,
37789      * the user will only be able to select from the items defined in the dropdown list.  This method
37790      * is the runtime equivalent of setting the 'editable' config option at config time.
37791      * @param {Boolean} value True to allow the user to directly edit the field text
37792      */
37793     setEditable : function(value){
37794         if(value == this.editable){
37795             return;
37796         }
37797         this.editable = value;
37798         if(!value){
37799             this.el.dom.setAttribute('readOnly', true);
37800             this.el.on('mousedown', this.onTriggerClick,  this);
37801             this.el.addClass('x-combo-noedit');
37802         }else{
37803             this.el.dom.setAttribute('readOnly', false);
37804             this.el.un('mousedown', this.onTriggerClick,  this);
37805             this.el.removeClass('x-combo-noedit');
37806         }
37807     },
37808
37809     // private
37810     onBeforeLoad : function(){
37811         if(!this.hasFocus){
37812             return;
37813         }
37814         this.innerList.update(this.loadingText ?
37815                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37816         this.restrictHeight();
37817         this.selectedIndex = -1;
37818     },
37819
37820     // private
37821     onLoad : function(){
37822         if(!this.hasFocus){
37823             return;
37824         }
37825         if(this.store.getCount() > 0){
37826             this.expand();
37827             this.restrictHeight();
37828             if(this.lastQuery == this.allQuery){
37829                 if(this.editable){
37830                     this.el.dom.select();
37831                 }
37832                 if(!this.selectByValue(this.value, true)){
37833                     this.select(0, true);
37834                 }
37835             }else{
37836                 this.selectNext();
37837                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37838                     this.taTask.delay(this.typeAheadDelay);
37839                 }
37840             }
37841         }else{
37842             this.onEmptyResults();
37843         }
37844         //this.el.focus();
37845     },
37846     // private
37847     onLoadException : function()
37848     {
37849         this.collapse();
37850         Roo.log(this.store.reader.jsonData);
37851         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37852             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37853         }
37854         
37855         
37856     },
37857     // private
37858     onTypeAhead : function(){
37859         if(this.store.getCount() > 0){
37860             var r = this.store.getAt(0);
37861             var newValue = r.data[this.displayField];
37862             var len = newValue.length;
37863             var selStart = this.getRawValue().length;
37864             if(selStart != len){
37865                 this.setRawValue(newValue);
37866                 this.selectText(selStart, newValue.length);
37867             }
37868         }
37869     },
37870
37871     // private
37872     onSelect : function(record, index){
37873         if(this.fireEvent('beforeselect', this, record, index) !== false){
37874             this.setFromData(index > -1 ? record.data : false);
37875             this.collapse();
37876             this.fireEvent('select', this, record, index);
37877         }
37878     },
37879
37880     /**
37881      * Returns the currently selected field value or empty string if no value is set.
37882      * @return {String} value The selected value
37883      */
37884     getValue : function(){
37885         if(this.valueField){
37886             return typeof this.value != 'undefined' ? this.value : '';
37887         }else{
37888             return Roo.form.ComboBox.superclass.getValue.call(this);
37889         }
37890     },
37891
37892     /**
37893      * Clears any text/value currently set in the field
37894      */
37895     clearValue : function(){
37896         if(this.hiddenField){
37897             this.hiddenField.value = '';
37898         }
37899         this.value = '';
37900         this.setRawValue('');
37901         this.lastSelectionText = '';
37902         this.applyEmptyText();
37903     },
37904
37905     /**
37906      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37907      * will be displayed in the field.  If the value does not match the data value of an existing item,
37908      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37909      * Otherwise the field will be blank (although the value will still be set).
37910      * @param {String} value The value to match
37911      */
37912     setValue : function(v){
37913         var text = v;
37914         if(this.valueField){
37915             var r = this.findRecord(this.valueField, v);
37916             if(r){
37917                 text = r.data[this.displayField];
37918             }else if(this.valueNotFoundText !== undefined){
37919                 text = this.valueNotFoundText;
37920             }
37921         }
37922         this.lastSelectionText = text;
37923         if(this.hiddenField){
37924             this.hiddenField.value = v;
37925         }
37926         Roo.form.ComboBox.superclass.setValue.call(this, text);
37927         this.value = v;
37928     },
37929     /**
37930      * @property {Object} the last set data for the element
37931      */
37932     
37933     lastData : false,
37934     /**
37935      * Sets the value of the field based on a object which is related to the record format for the store.
37936      * @param {Object} value the value to set as. or false on reset?
37937      */
37938     setFromData : function(o){
37939         var dv = ''; // display value
37940         var vv = ''; // value value..
37941         this.lastData = o;
37942         if (this.displayField) {
37943             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37944         } else {
37945             // this is an error condition!!!
37946             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37947         }
37948         
37949         if(this.valueField){
37950             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37951         }
37952         if(this.hiddenField){
37953             this.hiddenField.value = vv;
37954             
37955             this.lastSelectionText = dv;
37956             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37957             this.value = vv;
37958             return;
37959         }
37960         // no hidden field.. - we store the value in 'value', but still display
37961         // display field!!!!
37962         this.lastSelectionText = dv;
37963         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37964         this.value = vv;
37965         
37966         
37967     },
37968     // private
37969     reset : function(){
37970         // overridden so that last data is reset..
37971         this.setValue(this.originalValue);
37972         this.clearInvalid();
37973         this.lastData = false;
37974     },
37975     // private
37976     findRecord : function(prop, value){
37977         var record;
37978         if(this.store.getCount() > 0){
37979             this.store.each(function(r){
37980                 if(r.data[prop] == value){
37981                     record = r;
37982                     return false;
37983                 }
37984                 return true;
37985             });
37986         }
37987         return record;
37988     },
37989     
37990     getName: function()
37991     {
37992         // returns hidden if it's set..
37993         if (!this.rendered) {return ''};
37994         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37995         
37996     },
37997     // private
37998     onViewMove : function(e, t){
37999         this.inKeyMode = false;
38000     },
38001
38002     // private
38003     onViewOver : function(e, t){
38004         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38005             return;
38006         }
38007         var item = this.view.findItemFromChild(t);
38008         if(item){
38009             var index = this.view.indexOf(item);
38010             this.select(index, false);
38011         }
38012     },
38013
38014     // private
38015     onViewClick : function(doFocus)
38016     {
38017         var index = this.view.getSelectedIndexes()[0];
38018         var r = this.store.getAt(index);
38019         if(r){
38020             this.onSelect(r, index);
38021         }
38022         if(doFocus !== false && !this.blockFocus){
38023             this.el.focus();
38024         }
38025     },
38026
38027     // private
38028     restrictHeight : function(){
38029         this.innerList.dom.style.height = '';
38030         var inner = this.innerList.dom;
38031         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38032         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38033         this.list.beginUpdate();
38034         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38035         this.list.alignTo(this.el, this.listAlign);
38036         this.list.endUpdate();
38037     },
38038
38039     // private
38040     onEmptyResults : function(){
38041         this.collapse();
38042     },
38043
38044     /**
38045      * Returns true if the dropdown list is expanded, else false.
38046      */
38047     isExpanded : function(){
38048         return this.list.isVisible();
38049     },
38050
38051     /**
38052      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38053      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38054      * @param {String} value The data value of the item to select
38055      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38056      * selected item if it is not currently in view (defaults to true)
38057      * @return {Boolean} True if the value matched an item in the list, else false
38058      */
38059     selectByValue : function(v, scrollIntoView){
38060         if(v !== undefined && v !== null){
38061             var r = this.findRecord(this.valueField || this.displayField, v);
38062             if(r){
38063                 this.select(this.store.indexOf(r), scrollIntoView);
38064                 return true;
38065             }
38066         }
38067         return false;
38068     },
38069
38070     /**
38071      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38072      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38073      * @param {Number} index The zero-based index of the list item to select
38074      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38075      * selected item if it is not currently in view (defaults to true)
38076      */
38077     select : function(index, scrollIntoView){
38078         this.selectedIndex = index;
38079         this.view.select(index);
38080         if(scrollIntoView !== false){
38081             var el = this.view.getNode(index);
38082             if(el){
38083                 this.innerList.scrollChildIntoView(el, false);
38084             }
38085         }
38086     },
38087
38088     // private
38089     selectNext : function(){
38090         var ct = this.store.getCount();
38091         if(ct > 0){
38092             if(this.selectedIndex == -1){
38093                 this.select(0);
38094             }else if(this.selectedIndex < ct-1){
38095                 this.select(this.selectedIndex+1);
38096             }
38097         }
38098     },
38099
38100     // private
38101     selectPrev : function(){
38102         var ct = this.store.getCount();
38103         if(ct > 0){
38104             if(this.selectedIndex == -1){
38105                 this.select(0);
38106             }else if(this.selectedIndex != 0){
38107                 this.select(this.selectedIndex-1);
38108             }
38109         }
38110     },
38111
38112     // private
38113     onKeyUp : function(e){
38114         if(this.editable !== false && !e.isSpecialKey()){
38115             this.lastKey = e.getKey();
38116             this.dqTask.delay(this.queryDelay);
38117         }
38118     },
38119
38120     // private
38121     validateBlur : function(){
38122         return !this.list || !this.list.isVisible();   
38123     },
38124
38125     // private
38126     initQuery : function(){
38127         this.doQuery(this.getRawValue());
38128     },
38129
38130     // private
38131     doForce : function(){
38132         if(this.el.dom.value.length > 0){
38133             this.el.dom.value =
38134                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38135             this.applyEmptyText();
38136         }
38137     },
38138
38139     /**
38140      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38141      * query allowing the query action to be canceled if needed.
38142      * @param {String} query The SQL query to execute
38143      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38144      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38145      * saved in the current store (defaults to false)
38146      */
38147     doQuery : function(q, forceAll){
38148         if(q === undefined || q === null){
38149             q = '';
38150         }
38151         var qe = {
38152             query: q,
38153             forceAll: forceAll,
38154             combo: this,
38155             cancel:false
38156         };
38157         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38158             return false;
38159         }
38160         q = qe.query;
38161         forceAll = qe.forceAll;
38162         if(forceAll === true || (q.length >= this.minChars)){
38163             if(this.lastQuery != q || this.alwaysQuery){
38164                 this.lastQuery = q;
38165                 if(this.mode == 'local'){
38166                     this.selectedIndex = -1;
38167                     if(forceAll){
38168                         this.store.clearFilter();
38169                     }else{
38170                         this.store.filter(this.displayField, q);
38171                     }
38172                     this.onLoad();
38173                 }else{
38174                     this.store.baseParams[this.queryParam] = q;
38175                     this.store.load({
38176                         params: this.getParams(q)
38177                     });
38178                     this.expand();
38179                 }
38180             }else{
38181                 this.selectedIndex = -1;
38182                 this.onLoad();   
38183             }
38184         }
38185     },
38186
38187     // private
38188     getParams : function(q){
38189         var p = {};
38190         //p[this.queryParam] = q;
38191         if(this.pageSize){
38192             p.start = 0;
38193             p.limit = this.pageSize;
38194         }
38195         return p;
38196     },
38197
38198     /**
38199      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38200      */
38201     collapse : function(){
38202         if(!this.isExpanded()){
38203             return;
38204         }
38205         this.list.hide();
38206         Roo.get(document).un('mousedown', this.collapseIf, this);
38207         Roo.get(document).un('mousewheel', this.collapseIf, this);
38208         if (!this.editable) {
38209             Roo.get(document).un('keydown', this.listKeyPress, this);
38210         }
38211         this.fireEvent('collapse', this);
38212     },
38213
38214     // private
38215     collapseIf : function(e){
38216         if(!e.within(this.wrap) && !e.within(this.list)){
38217             this.collapse();
38218         }
38219     },
38220
38221     /**
38222      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38223      */
38224     expand : function(){
38225         if(this.isExpanded() || !this.hasFocus){
38226             return;
38227         }
38228         this.list.alignTo(this.el, this.listAlign);
38229         this.list.show();
38230         Roo.get(document).on('mousedown', this.collapseIf, this);
38231         Roo.get(document).on('mousewheel', this.collapseIf, this);
38232         if (!this.editable) {
38233             Roo.get(document).on('keydown', this.listKeyPress, this);
38234         }
38235         
38236         this.fireEvent('expand', this);
38237     },
38238
38239     // private
38240     // Implements the default empty TriggerField.onTriggerClick function
38241     onTriggerClick : function(){
38242         if(this.disabled){
38243             return;
38244         }
38245         if(this.isExpanded()){
38246             this.collapse();
38247             if (!this.blockFocus) {
38248                 this.el.focus();
38249             }
38250             
38251         }else {
38252             this.hasFocus = true;
38253             if(this.triggerAction == 'all') {
38254                 this.doQuery(this.allQuery, true);
38255             } else {
38256                 this.doQuery(this.getRawValue());
38257             }
38258             if (!this.blockFocus) {
38259                 this.el.focus();
38260             }
38261         }
38262     },
38263     listKeyPress : function(e)
38264     {
38265         //Roo.log('listkeypress');
38266         // scroll to first matching element based on key pres..
38267         if (e.isSpecialKey()) {
38268             return false;
38269         }
38270         var k = String.fromCharCode(e.getKey()).toUpperCase();
38271         //Roo.log(k);
38272         var match  = false;
38273         var csel = this.view.getSelectedNodes();
38274         var cselitem = false;
38275         if (csel.length) {
38276             var ix = this.view.indexOf(csel[0]);
38277             cselitem  = this.store.getAt(ix);
38278             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38279                 cselitem = false;
38280             }
38281             
38282         }
38283         
38284         this.store.each(function(v) { 
38285             if (cselitem) {
38286                 // start at existing selection.
38287                 if (cselitem.id == v.id) {
38288                     cselitem = false;
38289                 }
38290                 return;
38291             }
38292                 
38293             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38294                 match = this.store.indexOf(v);
38295                 return false;
38296             }
38297         }, this);
38298         
38299         if (match === false) {
38300             return true; // no more action?
38301         }
38302         // scroll to?
38303         this.view.select(match);
38304         var sn = Roo.get(this.view.getSelectedNodes()[0])
38305         sn.scrollIntoView(sn.dom.parentNode, false);
38306     }
38307
38308     /** 
38309     * @cfg {Boolean} grow 
38310     * @hide 
38311     */
38312     /** 
38313     * @cfg {Number} growMin 
38314     * @hide 
38315     */
38316     /** 
38317     * @cfg {Number} growMax 
38318     * @hide 
38319     */
38320     /**
38321      * @hide
38322      * @method autoSize
38323      */
38324 });/*
38325  * Based on:
38326  * Ext JS Library 1.1.1
38327  * Copyright(c) 2006-2007, Ext JS, LLC.
38328  *
38329  * Originally Released Under LGPL - original licence link has changed is not relivant.
38330  *
38331  * Fork - LGPL
38332  * <script type="text/javascript">
38333  */
38334 /**
38335  * @class Roo.form.Checkbox
38336  * @extends Roo.form.Field
38337  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38338  * @constructor
38339  * Creates a new Checkbox
38340  * @param {Object} config Configuration options
38341  */
38342 Roo.form.Checkbox = function(config){
38343     Roo.form.Checkbox.superclass.constructor.call(this, config);
38344     this.addEvents({
38345         /**
38346          * @event check
38347          * Fires when the checkbox is checked or unchecked.
38348              * @param {Roo.form.Checkbox} this This checkbox
38349              * @param {Boolean} checked The new checked value
38350              */
38351         check : true
38352     });
38353 };
38354
38355 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38356     /**
38357      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38358      */
38359     focusClass : undefined,
38360     /**
38361      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38362      */
38363     fieldClass: "x-form-field",
38364     /**
38365      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38366      */
38367     checked: false,
38368     /**
38369      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38370      * {tag: "input", type: "checkbox", autocomplete: "off"})
38371      */
38372     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38373     /**
38374      * @cfg {String} boxLabel The text that appears beside the checkbox
38375      */
38376     boxLabel : "",
38377     /**
38378      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38379      */  
38380     inputValue : '1',
38381     /**
38382      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38383      */
38384      valueOff: '0', // value when not checked..
38385
38386     actionMode : 'viewEl', 
38387     //
38388     // private
38389     itemCls : 'x-menu-check-item x-form-item',
38390     groupClass : 'x-menu-group-item',
38391     inputType : 'hidden',
38392     
38393     
38394     inSetChecked: false, // check that we are not calling self...
38395     
38396     inputElement: false, // real input element?
38397     basedOn: false, // ????
38398     
38399     isFormField: true, // not sure where this is needed!!!!
38400
38401     onResize : function(){
38402         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38403         if(!this.boxLabel){
38404             this.el.alignTo(this.wrap, 'c-c');
38405         }
38406     },
38407
38408     initEvents : function(){
38409         Roo.form.Checkbox.superclass.initEvents.call(this);
38410         this.el.on("click", this.onClick,  this);
38411         this.el.on("change", this.onClick,  this);
38412     },
38413
38414
38415     getResizeEl : function(){
38416         return this.wrap;
38417     },
38418
38419     getPositionEl : function(){
38420         return this.wrap;
38421     },
38422
38423     // private
38424     onRender : function(ct, position){
38425         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38426         /*
38427         if(this.inputValue !== undefined){
38428             this.el.dom.value = this.inputValue;
38429         }
38430         */
38431         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38432         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38433         var viewEl = this.wrap.createChild({ 
38434             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38435         this.viewEl = viewEl;   
38436         this.wrap.on('click', this.onClick,  this); 
38437         
38438         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38439         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38440         
38441         
38442         
38443         if(this.boxLabel){
38444             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38445         //    viewEl.on('click', this.onClick,  this); 
38446         }
38447         //if(this.checked){
38448             this.setChecked(this.checked);
38449         //}else{
38450             //this.checked = this.el.dom;
38451         //}
38452
38453     },
38454
38455     // private
38456     initValue : Roo.emptyFn,
38457
38458     /**
38459      * Returns the checked state of the checkbox.
38460      * @return {Boolean} True if checked, else false
38461      */
38462     getValue : function(){
38463         if(this.el){
38464             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38465         }
38466         return this.valueOff;
38467         
38468     },
38469
38470         // private
38471     onClick : function(){ 
38472         this.setChecked(!this.checked);
38473
38474         //if(this.el.dom.checked != this.checked){
38475         //    this.setValue(this.el.dom.checked);
38476        // }
38477     },
38478
38479     /**
38480      * Sets the checked state of the checkbox.
38481      * On is always based on a string comparison between inputValue and the param.
38482      * @param {Boolean/String} value - the value to set 
38483      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38484      */
38485     setValue : function(v,suppressEvent){
38486         
38487         
38488         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38489         //if(this.el && this.el.dom){
38490         //    this.el.dom.checked = this.checked;
38491         //    this.el.dom.defaultChecked = this.checked;
38492         //}
38493         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38494         //this.fireEvent("check", this, this.checked);
38495     },
38496     // private..
38497     setChecked : function(state,suppressEvent)
38498     {
38499         if (this.inSetChecked) {
38500             this.checked = state;
38501             return;
38502         }
38503         
38504     
38505         if(this.wrap){
38506             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38507         }
38508         this.checked = state;
38509         if(suppressEvent !== true){
38510             this.fireEvent('check', this, state);
38511         }
38512         this.inSetChecked = true;
38513         this.el.dom.value = state ? this.inputValue : this.valueOff;
38514         this.inSetChecked = false;
38515         
38516     },
38517     // handle setting of hidden value by some other method!!?!?
38518     setFromHidden: function()
38519     {
38520         if(!this.el){
38521             return;
38522         }
38523         //console.log("SET FROM HIDDEN");
38524         //alert('setFrom hidden');
38525         this.setValue(this.el.dom.value);
38526     },
38527     
38528     onDestroy : function()
38529     {
38530         if(this.viewEl){
38531             Roo.get(this.viewEl).remove();
38532         }
38533          
38534         Roo.form.Checkbox.superclass.onDestroy.call(this);
38535     }
38536
38537 });/*
38538  * Based on:
38539  * Ext JS Library 1.1.1
38540  * Copyright(c) 2006-2007, Ext JS, LLC.
38541  *
38542  * Originally Released Under LGPL - original licence link has changed is not relivant.
38543  *
38544  * Fork - LGPL
38545  * <script type="text/javascript">
38546  */
38547  
38548 /**
38549  * @class Roo.form.Radio
38550  * @extends Roo.form.Checkbox
38551  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38552  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38553  * @constructor
38554  * Creates a new Radio
38555  * @param {Object} config Configuration options
38556  */
38557 Roo.form.Radio = function(){
38558     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38559 };
38560 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38561     inputType: 'radio',
38562
38563     /**
38564      * If this radio is part of a group, it will return the selected value
38565      * @return {String}
38566      */
38567     getGroupValue : function(){
38568         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38569     }
38570 });//<script type="text/javascript">
38571
38572 /*
38573  * Ext JS Library 1.1.1
38574  * Copyright(c) 2006-2007, Ext JS, LLC.
38575  * licensing@extjs.com
38576  * 
38577  * http://www.extjs.com/license
38578  */
38579  
38580  /*
38581   * 
38582   * Known bugs:
38583   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38584   * - IE ? - no idea how much works there.
38585   * 
38586   * 
38587   * 
38588   */
38589  
38590
38591 /**
38592  * @class Ext.form.HtmlEditor
38593  * @extends Ext.form.Field
38594  * Provides a lightweight HTML Editor component.
38595  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38596  * 
38597  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38598  * supported by this editor.</b><br/><br/>
38599  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38600  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38601  */
38602 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38603       /**
38604      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38605      */
38606     toolbars : false,
38607     /**
38608      * @cfg {String} createLinkText The default text for the create link prompt
38609      */
38610     createLinkText : 'Please enter the URL for the link:',
38611     /**
38612      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38613      */
38614     defaultLinkValue : 'http:/'+'/',
38615    
38616      /**
38617      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38618      *                        Roo.resizable.
38619      */
38620     resizable : false,
38621      /**
38622      * @cfg {Number} height (in pixels)
38623      */   
38624     height: 300,
38625    /**
38626      * @cfg {Number} width (in pixels)
38627      */   
38628     width: 500,
38629     
38630     /**
38631      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38632      * 
38633      */
38634     stylesheets: false,
38635     
38636     // id of frame..
38637     frameId: false,
38638     
38639     // private properties
38640     validationEvent : false,
38641     deferHeight: true,
38642     initialized : false,
38643     activated : false,
38644     sourceEditMode : false,
38645     onFocus : Roo.emptyFn,
38646     iframePad:3,
38647     hideMode:'offsets',
38648     
38649     defaultAutoCreate : { // modified by initCompnoent..
38650         tag: "textarea",
38651         style:"width:500px;height:300px;",
38652         autocomplete: "off"
38653     },
38654
38655     // private
38656     initComponent : function(){
38657         this.addEvents({
38658             /**
38659              * @event initialize
38660              * Fires when the editor is fully initialized (including the iframe)
38661              * @param {HtmlEditor} this
38662              */
38663             initialize: true,
38664             /**
38665              * @event activate
38666              * Fires when the editor is first receives the focus. Any insertion must wait
38667              * until after this event.
38668              * @param {HtmlEditor} this
38669              */
38670             activate: true,
38671              /**
38672              * @event beforesync
38673              * Fires before the textarea is updated with content from the editor iframe. Return false
38674              * to cancel the sync.
38675              * @param {HtmlEditor} this
38676              * @param {String} html
38677              */
38678             beforesync: true,
38679              /**
38680              * @event beforepush
38681              * Fires before the iframe editor is updated with content from the textarea. Return false
38682              * to cancel the push.
38683              * @param {HtmlEditor} this
38684              * @param {String} html
38685              */
38686             beforepush: true,
38687              /**
38688              * @event sync
38689              * Fires when the textarea is updated with content from the editor iframe.
38690              * @param {HtmlEditor} this
38691              * @param {String} html
38692              */
38693             sync: true,
38694              /**
38695              * @event push
38696              * Fires when the iframe editor is updated with content from the textarea.
38697              * @param {HtmlEditor} this
38698              * @param {String} html
38699              */
38700             push: true,
38701              /**
38702              * @event editmodechange
38703              * Fires when the editor switches edit modes
38704              * @param {HtmlEditor} this
38705              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38706              */
38707             editmodechange: true,
38708             /**
38709              * @event editorevent
38710              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38711              * @param {HtmlEditor} this
38712              */
38713             editorevent: true
38714         });
38715         this.defaultAutoCreate =  {
38716             tag: "textarea",
38717             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38718             autocomplete: "off"
38719         };
38720     },
38721
38722     /**
38723      * Protected method that will not generally be called directly. It
38724      * is called when the editor creates its toolbar. Override this method if you need to
38725      * add custom toolbar buttons.
38726      * @param {HtmlEditor} editor
38727      */
38728     createToolbar : function(editor){
38729         if (!editor.toolbars || !editor.toolbars.length) {
38730             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38731         }
38732         
38733         for (var i =0 ; i < editor.toolbars.length;i++) {
38734             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38735             editor.toolbars[i].init(editor);
38736         }
38737          
38738         
38739     },
38740
38741     /**
38742      * Protected method that will not generally be called directly. It
38743      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38744      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38745      */
38746     getDocMarkup : function(){
38747         // body styles..
38748         var st = '';
38749         if (this.stylesheets === false) {
38750             
38751             Roo.get(document.head).select('style').each(function(node) {
38752                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38753             });
38754             
38755             Roo.get(document.head).select('link').each(function(node) { 
38756                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38757             });
38758             
38759         } else if (!this.stylesheets.length) {
38760                 // simple..
38761                 st = '<style type="text/css">' +
38762                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38763                    '</style>';
38764         } else {
38765             Roo.each(this.stylesheets, function(s) {
38766                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38767             });
38768             
38769         }
38770         
38771         return '<html><head>' + st  +
38772             //<style type="text/css">' +
38773             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38774             //'</style>' +
38775             ' </head><body></body></html>';
38776     },
38777
38778     // private
38779     onRender : function(ct, position)
38780     {
38781         var _t = this;
38782         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38783         this.el.dom.style.border = '0 none';
38784         this.el.dom.setAttribute('tabIndex', -1);
38785         this.el.addClass('x-hidden');
38786         if(Roo.isIE){ // fix IE 1px bogus margin
38787             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38788         }
38789         this.wrap = this.el.wrap({
38790             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38791         });
38792         
38793         if (this.resizable) {
38794             this.resizeEl = new Roo.Resizable(this.wrap, {
38795                 pinned : true,
38796                 wrap: true,
38797                 dynamic : true,
38798                 minHeight : this.height,
38799                 height: this.height,
38800                 handles : this.resizable,
38801                 width: this.width,
38802                 listeners : {
38803                     resize : function(r, w, h) {
38804                         _t.onResize(w,h); // -something
38805                     }
38806                 }
38807             });
38808             
38809         }
38810
38811         this.frameId = Roo.id();
38812         
38813         this.createToolbar(this);
38814         
38815       
38816         
38817         var iframe = this.wrap.createChild({
38818             tag: 'iframe',
38819             id: this.frameId,
38820             name: this.frameId,
38821             frameBorder : 'no',
38822             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38823         }, this.el
38824         );
38825         
38826        // console.log(iframe);
38827         //this.wrap.dom.appendChild(iframe);
38828
38829         this.iframe = iframe.dom;
38830
38831          this.assignDocWin();
38832         
38833         this.doc.designMode = 'on';
38834        
38835         this.doc.open();
38836         this.doc.write(this.getDocMarkup());
38837         this.doc.close();
38838
38839         
38840         var task = { // must defer to wait for browser to be ready
38841             run : function(){
38842                 //console.log("run task?" + this.doc.readyState);
38843                 this.assignDocWin();
38844                 if(this.doc.body || this.doc.readyState == 'complete'){
38845                     try {
38846                         this.doc.designMode="on";
38847                     } catch (e) {
38848                         return;
38849                     }
38850                     Roo.TaskMgr.stop(task);
38851                     this.initEditor.defer(10, this);
38852                 }
38853             },
38854             interval : 10,
38855             duration:10000,
38856             scope: this
38857         };
38858         Roo.TaskMgr.start(task);
38859
38860         if(!this.width){
38861             this.setSize(this.wrap.getSize());
38862         }
38863         if (this.resizeEl) {
38864             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38865             // should trigger onReize..
38866         }
38867     },
38868
38869     // private
38870     onResize : function(w, h)
38871     {
38872         //Roo.log('resize: ' +w + ',' + h );
38873         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38874         if(this.el && this.iframe){
38875             if(typeof w == 'number'){
38876                 var aw = w - this.wrap.getFrameWidth('lr');
38877                 this.el.setWidth(this.adjustWidth('textarea', aw));
38878                 this.iframe.style.width = aw + 'px';
38879             }
38880             if(typeof h == 'number'){
38881                 var tbh = 0;
38882                 for (var i =0; i < this.toolbars.length;i++) {
38883                     // fixme - ask toolbars for heights?
38884                     tbh += this.toolbars[i].tb.el.getHeight();
38885                     if (this.toolbars[i].footer) {
38886                         tbh += this.toolbars[i].footer.el.getHeight();
38887                     }
38888                 }
38889                 
38890                 
38891                 
38892                 
38893                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38894                 ah -= 5; // knock a few pixes off for look..
38895                 this.el.setHeight(this.adjustWidth('textarea', ah));
38896                 this.iframe.style.height = ah + 'px';
38897                 if(this.doc){
38898                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38899                 }
38900             }
38901         }
38902     },
38903
38904     /**
38905      * Toggles the editor between standard and source edit mode.
38906      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38907      */
38908     toggleSourceEdit : function(sourceEditMode){
38909         
38910         this.sourceEditMode = sourceEditMode === true;
38911         
38912         if(this.sourceEditMode){
38913           
38914             this.syncValue();
38915             this.iframe.className = 'x-hidden';
38916             this.el.removeClass('x-hidden');
38917             this.el.dom.removeAttribute('tabIndex');
38918             this.el.focus();
38919         }else{
38920              
38921             this.pushValue();
38922             this.iframe.className = '';
38923             this.el.addClass('x-hidden');
38924             this.el.dom.setAttribute('tabIndex', -1);
38925             this.deferFocus();
38926         }
38927         this.setSize(this.wrap.getSize());
38928         this.fireEvent('editmodechange', this, this.sourceEditMode);
38929     },
38930
38931     // private used internally
38932     createLink : function(){
38933         var url = prompt(this.createLinkText, this.defaultLinkValue);
38934         if(url && url != 'http:/'+'/'){
38935             this.relayCmd('createlink', url);
38936         }
38937     },
38938
38939     // private (for BoxComponent)
38940     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38941
38942     // private (for BoxComponent)
38943     getResizeEl : function(){
38944         return this.wrap;
38945     },
38946
38947     // private (for BoxComponent)
38948     getPositionEl : function(){
38949         return this.wrap;
38950     },
38951
38952     // private
38953     initEvents : function(){
38954         this.originalValue = this.getValue();
38955     },
38956
38957     /**
38958      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38959      * @method
38960      */
38961     markInvalid : Roo.emptyFn,
38962     /**
38963      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38964      * @method
38965      */
38966     clearInvalid : Roo.emptyFn,
38967
38968     setValue : function(v){
38969         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38970         this.pushValue();
38971     },
38972
38973     /**
38974      * Protected method that will not generally be called directly. If you need/want
38975      * custom HTML cleanup, this is the method you should override.
38976      * @param {String} html The HTML to be cleaned
38977      * return {String} The cleaned HTML
38978      */
38979     cleanHtml : function(html){
38980         html = String(html);
38981         if(html.length > 5){
38982             if(Roo.isSafari){ // strip safari nonsense
38983                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38984             }
38985         }
38986         if(html == '&nbsp;'){
38987             html = '';
38988         }
38989         return html;
38990     },
38991
38992     /**
38993      * Protected method that will not generally be called directly. Syncs the contents
38994      * of the editor iframe with the textarea.
38995      */
38996     syncValue : function(){
38997         if(this.initialized){
38998             var bd = (this.doc.body || this.doc.documentElement);
38999             //this.cleanUpPaste();
39000             var html = bd.innerHTML;
39001             if(Roo.isSafari){
39002                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39003                 var m = bs.match(/text-align:(.*?);/i);
39004                 if(m && m[1]){
39005                     html = '<div style="'+m[0]+'">' + html + '</div>';
39006                 }
39007             }
39008             html = this.cleanHtml(html);
39009             if(this.fireEvent('beforesync', this, html) !== false){
39010                 this.el.dom.value = html;
39011                 this.fireEvent('sync', this, html);
39012             }
39013         }
39014     },
39015
39016     /**
39017      * Protected method that will not generally be called directly. Pushes the value of the textarea
39018      * into the iframe editor.
39019      */
39020     pushValue : function(){
39021         if(this.initialized){
39022             var v = this.el.dom.value;
39023             if(v.length < 1){
39024                 v = '&#160;';
39025             }
39026             
39027             if(this.fireEvent('beforepush', this, v) !== false){
39028                 var d = (this.doc.body || this.doc.documentElement);
39029                 d.innerHTML = v;
39030                 this.cleanUpPaste();
39031                 this.el.dom.value = d.innerHTML;
39032                 this.fireEvent('push', this, v);
39033             }
39034         }
39035     },
39036
39037     // private
39038     deferFocus : function(){
39039         this.focus.defer(10, this);
39040     },
39041
39042     // doc'ed in Field
39043     focus : function(){
39044         if(this.win && !this.sourceEditMode){
39045             this.win.focus();
39046         }else{
39047             this.el.focus();
39048         }
39049     },
39050     
39051     assignDocWin: function()
39052     {
39053         var iframe = this.iframe;
39054         
39055          if(Roo.isIE){
39056             this.doc = iframe.contentWindow.document;
39057             this.win = iframe.contentWindow;
39058         } else {
39059             if (!Roo.get(this.frameId)) {
39060                 return;
39061             }
39062             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39063             this.win = Roo.get(this.frameId).dom.contentWindow;
39064         }
39065     },
39066     
39067     // private
39068     initEditor : function(){
39069         //console.log("INIT EDITOR");
39070         this.assignDocWin();
39071         
39072         
39073         
39074         this.doc.designMode="on";
39075         this.doc.open();
39076         this.doc.write(this.getDocMarkup());
39077         this.doc.close();
39078         
39079         var dbody = (this.doc.body || this.doc.documentElement);
39080         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39081         // this copies styles from the containing element into thsi one..
39082         // not sure why we need all of this..
39083         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39084         ss['background-attachment'] = 'fixed'; // w3c
39085         dbody.bgProperties = 'fixed'; // ie
39086         Roo.DomHelper.applyStyles(dbody, ss);
39087         Roo.EventManager.on(this.doc, {
39088             //'mousedown': this.onEditorEvent,
39089             'mouseup': this.onEditorEvent,
39090             'dblclick': this.onEditorEvent,
39091             'click': this.onEditorEvent,
39092             'keyup': this.onEditorEvent,
39093             buffer:100,
39094             scope: this
39095         });
39096         if(Roo.isGecko){
39097             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39098         }
39099         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39100             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39101         }
39102         this.initialized = true;
39103
39104         this.fireEvent('initialize', this);
39105         this.pushValue();
39106     },
39107
39108     // private
39109     onDestroy : function(){
39110         
39111         
39112         
39113         if(this.rendered){
39114             
39115             for (var i =0; i < this.toolbars.length;i++) {
39116                 // fixme - ask toolbars for heights?
39117                 this.toolbars[i].onDestroy();
39118             }
39119             
39120             this.wrap.dom.innerHTML = '';
39121             this.wrap.remove();
39122         }
39123     },
39124
39125     // private
39126     onFirstFocus : function(){
39127         
39128         this.assignDocWin();
39129         
39130         
39131         this.activated = true;
39132         for (var i =0; i < this.toolbars.length;i++) {
39133             this.toolbars[i].onFirstFocus();
39134         }
39135        
39136         if(Roo.isGecko){ // prevent silly gecko errors
39137             this.win.focus();
39138             var s = this.win.getSelection();
39139             if(!s.focusNode || s.focusNode.nodeType != 3){
39140                 var r = s.getRangeAt(0);
39141                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39142                 r.collapse(true);
39143                 this.deferFocus();
39144             }
39145             try{
39146                 this.execCmd('useCSS', true);
39147                 this.execCmd('styleWithCSS', false);
39148             }catch(e){}
39149         }
39150         this.fireEvent('activate', this);
39151     },
39152
39153     // private
39154     adjustFont: function(btn){
39155         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39156         //if(Roo.isSafari){ // safari
39157         //    adjust *= 2;
39158        // }
39159         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39160         if(Roo.isSafari){ // safari
39161             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39162             v =  (v < 10) ? 10 : v;
39163             v =  (v > 48) ? 48 : v;
39164             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39165             
39166         }
39167         
39168         
39169         v = Math.max(1, v+adjust);
39170         
39171         this.execCmd('FontSize', v  );
39172     },
39173
39174     onEditorEvent : function(e){
39175         this.fireEvent('editorevent', this, e);
39176       //  this.updateToolbar();
39177         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39178     },
39179
39180     insertTag : function(tg)
39181     {
39182         // could be a bit smarter... -> wrap the current selected tRoo..
39183         
39184         this.execCmd("formatblock",   tg);
39185         
39186     },
39187     
39188     insertText : function(txt)
39189     {
39190         
39191         
39192         range = this.createRange();
39193         range.deleteContents();
39194                //alert(Sender.getAttribute('label'));
39195                
39196         range.insertNode(this.doc.createTextNode(txt));
39197     } ,
39198     
39199     // private
39200     relayBtnCmd : function(btn){
39201         this.relayCmd(btn.cmd);
39202     },
39203
39204     /**
39205      * Executes a Midas editor command on the editor document and performs necessary focus and
39206      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39207      * @param {String} cmd The Midas command
39208      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39209      */
39210     relayCmd : function(cmd, value){
39211         this.win.focus();
39212         this.execCmd(cmd, value);
39213         this.fireEvent('editorevent', this);
39214         //this.updateToolbar();
39215         this.deferFocus();
39216     },
39217
39218     /**
39219      * Executes a Midas editor command directly on the editor document.
39220      * For visual commands, you should use {@link #relayCmd} instead.
39221      * <b>This should only be called after the editor is initialized.</b>
39222      * @param {String} cmd The Midas command
39223      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39224      */
39225     execCmd : function(cmd, value){
39226         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39227         this.syncValue();
39228     },
39229
39230    
39231     /**
39232      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39233      * to insert tRoo.
39234      * @param {String} text
39235      */
39236     insertAtCursor : function(text){
39237         if(!this.activated){
39238             return;
39239         }
39240         if(Roo.isIE){
39241             this.win.focus();
39242             var r = this.doc.selection.createRange();
39243             if(r){
39244                 r.collapse(true);
39245                 r.pasteHTML(text);
39246                 this.syncValue();
39247                 this.deferFocus();
39248             }
39249         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39250             this.win.focus();
39251             this.execCmd('InsertHTML', text);
39252             this.deferFocus();
39253         }
39254     },
39255  // private
39256     mozKeyPress : function(e){
39257         if(e.ctrlKey){
39258             var c = e.getCharCode(), cmd;
39259           
39260             if(c > 0){
39261                 c = String.fromCharCode(c).toLowerCase();
39262                 switch(c){
39263                     case 'b':
39264                         cmd = 'bold';
39265                     break;
39266                     case 'i':
39267                         cmd = 'italic';
39268                     break;
39269                     case 'u':
39270                         cmd = 'underline';
39271                         break;
39272                     case 'v':
39273                         this.cleanUpPaste.defer(100, this);
39274                         return;
39275                     break;
39276                 }
39277                 if(cmd){
39278                     this.win.focus();
39279                     this.execCmd(cmd);
39280                     this.deferFocus();
39281                     e.preventDefault();
39282                 }
39283                 
39284             }
39285         }
39286     },
39287
39288     // private
39289     fixKeys : function(){ // load time branching for fastest keydown performance
39290         if(Roo.isIE){
39291             return function(e){
39292                 var k = e.getKey(), r;
39293                 if(k == e.TAB){
39294                     e.stopEvent();
39295                     r = this.doc.selection.createRange();
39296                     if(r){
39297                         r.collapse(true);
39298                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39299                         this.deferFocus();
39300                     }
39301                     return;
39302                 }
39303                 
39304                 if(k == e.ENTER){
39305                     r = this.doc.selection.createRange();
39306                     if(r){
39307                         var target = r.parentElement();
39308                         if(!target || target.tagName.toLowerCase() != 'li'){
39309                             e.stopEvent();
39310                             r.pasteHTML('<br />');
39311                             r.collapse(false);
39312                             r.select();
39313                         }
39314                     }
39315                 }
39316                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39317                     this.cleanUpPaste.defer(100, this);
39318                     return;
39319                 }
39320                 
39321                 
39322             };
39323         }else if(Roo.isOpera){
39324             return function(e){
39325                 var k = e.getKey();
39326                 if(k == e.TAB){
39327                     e.stopEvent();
39328                     this.win.focus();
39329                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39330                     this.deferFocus();
39331                 }
39332                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39333                     this.cleanUpPaste.defer(100, this);
39334                     return;
39335                 }
39336                 
39337             };
39338         }else if(Roo.isSafari){
39339             return function(e){
39340                 var k = e.getKey();
39341                 
39342                 if(k == e.TAB){
39343                     e.stopEvent();
39344                     this.execCmd('InsertText','\t');
39345                     this.deferFocus();
39346                     return;
39347                 }
39348                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39349                     this.cleanUpPaste.defer(100, this);
39350                     return;
39351                 }
39352                 
39353              };
39354         }
39355     }(),
39356     
39357     getAllAncestors: function()
39358     {
39359         var p = this.getSelectedNode();
39360         var a = [];
39361         if (!p) {
39362             a.push(p); // push blank onto stack..
39363             p = this.getParentElement();
39364         }
39365         
39366         
39367         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39368             a.push(p);
39369             p = p.parentNode;
39370         }
39371         a.push(this.doc.body);
39372         return a;
39373     },
39374     lastSel : false,
39375     lastSelNode : false,
39376     
39377     
39378     getSelection : function() 
39379     {
39380         this.assignDocWin();
39381         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39382     },
39383     
39384     getSelectedNode: function() 
39385     {
39386         // this may only work on Gecko!!!
39387         
39388         // should we cache this!!!!
39389         
39390         
39391         
39392          
39393         var range = this.createRange(this.getSelection()).cloneRange();
39394         
39395         if (Roo.isIE) {
39396             var parent = range.parentElement();
39397             while (true) {
39398                 var testRange = range.duplicate();
39399                 testRange.moveToElementText(parent);
39400                 if (testRange.inRange(range)) {
39401                     break;
39402                 }
39403                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39404                     break;
39405                 }
39406                 parent = parent.parentElement;
39407             }
39408             return parent;
39409         }
39410         
39411         // is ancestor a text element.
39412         var ac =  range.commonAncestorContainer;
39413         if (ac.nodeType == 3) {
39414             ac = ac.parentNode;
39415         }
39416         
39417         var ar = ac.childNodes;
39418          
39419         var nodes = [];
39420         var other_nodes = [];
39421         var has_other_nodes = false;
39422         for (var i=0;i<ar.length;i++) {
39423             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39424                 continue;
39425             }
39426             // fullly contained node.
39427             
39428             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39429                 nodes.push(ar[i]);
39430                 continue;
39431             }
39432             
39433             // probably selected..
39434             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39435                 other_nodes.push(ar[i]);
39436                 continue;
39437             }
39438             // outer..
39439             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39440                 continue;
39441             }
39442             
39443             
39444             has_other_nodes = true;
39445         }
39446         if (!nodes.length && other_nodes.length) {
39447             nodes= other_nodes;
39448         }
39449         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39450             return false;
39451         }
39452         
39453         return nodes[0];
39454     },
39455     createRange: function(sel)
39456     {
39457         // this has strange effects when using with 
39458         // top toolbar - not sure if it's a great idea.
39459         //this.editor.contentWindow.focus();
39460         if (typeof sel != "undefined") {
39461             try {
39462                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39463             } catch(e) {
39464                 return this.doc.createRange();
39465             }
39466         } else {
39467             return this.doc.createRange();
39468         }
39469     },
39470     getParentElement: function()
39471     {
39472         
39473         this.assignDocWin();
39474         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39475         
39476         var range = this.createRange(sel);
39477          
39478         try {
39479             var p = range.commonAncestorContainer;
39480             while (p.nodeType == 3) { // text node
39481                 p = p.parentNode;
39482             }
39483             return p;
39484         } catch (e) {
39485             return null;
39486         }
39487     
39488     },
39489     /***
39490      *
39491      * Range intersection.. the hard stuff...
39492      *  '-1' = before
39493      *  '0' = hits..
39494      *  '1' = after.
39495      *         [ -- selected range --- ]
39496      *   [fail]                        [fail]
39497      *
39498      *    basically..
39499      *      if end is before start or  hits it. fail.
39500      *      if start is after end or hits it fail.
39501      *
39502      *   if either hits (but other is outside. - then it's not 
39503      *   
39504      *    
39505      **/
39506     
39507     
39508     // @see http://www.thismuchiknow.co.uk/?p=64.
39509     rangeIntersectsNode : function(range, node)
39510     {
39511         var nodeRange = node.ownerDocument.createRange();
39512         try {
39513             nodeRange.selectNode(node);
39514         } catch (e) {
39515             nodeRange.selectNodeContents(node);
39516         }
39517     
39518         var rangeStartRange = range.cloneRange();
39519         rangeStartRange.collapse(true);
39520     
39521         var rangeEndRange = range.cloneRange();
39522         rangeEndRange.collapse(false);
39523     
39524         var nodeStartRange = nodeRange.cloneRange();
39525         nodeStartRange.collapse(true);
39526     
39527         var nodeEndRange = nodeRange.cloneRange();
39528         nodeEndRange.collapse(false);
39529     
39530         return rangeStartRange.compareBoundaryPoints(
39531                  Range.START_TO_START, nodeEndRange) == -1 &&
39532                rangeEndRange.compareBoundaryPoints(
39533                  Range.START_TO_START, nodeStartRange) == 1;
39534         
39535          
39536     },
39537     rangeCompareNode : function(range, node)
39538     {
39539         var nodeRange = node.ownerDocument.createRange();
39540         try {
39541             nodeRange.selectNode(node);
39542         } catch (e) {
39543             nodeRange.selectNodeContents(node);
39544         }
39545         
39546         
39547         range.collapse(true);
39548     
39549         nodeRange.collapse(true);
39550      
39551         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39552         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39553          
39554         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39555         
39556         var nodeIsBefore   =  ss == 1;
39557         var nodeIsAfter    = ee == -1;
39558         
39559         if (nodeIsBefore && nodeIsAfter)
39560             return 0; // outer
39561         if (!nodeIsBefore && nodeIsAfter)
39562             return 1; //right trailed.
39563         
39564         if (nodeIsBefore && !nodeIsAfter)
39565             return 2;  // left trailed.
39566         // fully contined.
39567         return 3;
39568     },
39569
39570     // private? - in a new class?
39571     cleanUpPaste :  function()
39572     {
39573         // cleans up the whole document..
39574          Roo.log('cleanuppaste');
39575         this.cleanUpChildren(this.doc.body);
39576         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39577         if (clean != this.doc.body.innerHTML) {
39578             this.doc.body.innerHTML = clean;
39579         }
39580         
39581     },
39582     
39583     cleanWordChars : function(input) {
39584         var he = Roo.form.HtmlEditor;
39585     
39586         var output = input;
39587         Roo.each(he.swapCodes, function(sw) { 
39588         
39589             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39590             output = output.replace(swapper, sw[1]);
39591         });
39592         return output;
39593     },
39594     
39595     
39596     cleanUpChildren : function (n)
39597     {
39598         if (!n.childNodes.length) {
39599             return;
39600         }
39601         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39602            this.cleanUpChild(n.childNodes[i]);
39603         }
39604     },
39605     
39606     
39607         
39608     
39609     cleanUpChild : function (node)
39610     {
39611         //console.log(node);
39612         if (node.nodeName == "#text") {
39613             // clean up silly Windows -- stuff?
39614             return; 
39615         }
39616         if (node.nodeName == "#comment") {
39617             node.parentNode.removeChild(node);
39618             // clean up silly Windows -- stuff?
39619             return; 
39620         }
39621         
39622         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39623             // remove node.
39624             node.parentNode.removeChild(node);
39625             return;
39626             
39627         }
39628         
39629         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39630         
39631         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39632         
39633         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39634             remove_keep_children = true;
39635         }
39636         
39637         if (remove_keep_children) {
39638             this.cleanUpChildren(node);
39639             // inserts everything just before this node...
39640             while (node.childNodes.length) {
39641                 var cn = node.childNodes[0];
39642                 node.removeChild(cn);
39643                 node.parentNode.insertBefore(cn, node);
39644             }
39645             node.parentNode.removeChild(node);
39646             return;
39647         }
39648         
39649         if (!node.attributes || !node.attributes.length) {
39650             this.cleanUpChildren(node);
39651             return;
39652         }
39653         
39654         function cleanAttr(n,v)
39655         {
39656             
39657             if (v.match(/^\./) || v.match(/^\//)) {
39658                 return;
39659             }
39660             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39661                 return;
39662             }
39663             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39664             node.removeAttribute(n);
39665             
39666         }
39667         
39668         function cleanStyle(n,v)
39669         {
39670             if (v.match(/expression/)) { //XSS?? should we even bother..
39671                 node.removeAttribute(n);
39672                 return;
39673             }
39674             
39675             
39676             var parts = v.split(/;/);
39677             Roo.each(parts, function(p) {
39678                 p = p.replace(/\s+/g,'');
39679                 if (!p.length) {
39680                     return true;
39681                 }
39682                 var l = p.split(':').shift().replace(/\s+/g,'');
39683                 
39684                 // only allow 'c whitelisted system attributes'
39685                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39686                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39687                     node.removeAttribute(n);
39688                     return false;
39689                 }
39690                 return true;
39691             });
39692             
39693             
39694         }
39695         
39696         
39697         for (var i = node.attributes.length-1; i > -1 ; i--) {
39698             var a = node.attributes[i];
39699             //console.log(a);
39700             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39701                 node.removeAttribute(a.name);
39702                 return;
39703             }
39704             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39705                 cleanAttr(a.name,a.value); // fixme..
39706                 return;
39707             }
39708             if (a.name == 'style') {
39709                 cleanStyle(a.name,a.value);
39710             }
39711             /// clean up MS crap..
39712             // tecnically this should be a list of valid class'es..
39713             
39714             
39715             if (a.name == 'class') {
39716                 if (a.value.match(/^Mso/)) {
39717                     node.className = '';
39718                 }
39719                 
39720                 if (a.value.match(/body/)) {
39721                     node.className = '';
39722                 }
39723             }
39724             
39725             // style cleanup!?
39726             // class cleanup?
39727             
39728         }
39729         
39730         
39731         this.cleanUpChildren(node);
39732         
39733         
39734     }
39735     
39736     
39737     // hide stuff that is not compatible
39738     /**
39739      * @event blur
39740      * @hide
39741      */
39742     /**
39743      * @event change
39744      * @hide
39745      */
39746     /**
39747      * @event focus
39748      * @hide
39749      */
39750     /**
39751      * @event specialkey
39752      * @hide
39753      */
39754     /**
39755      * @cfg {String} fieldClass @hide
39756      */
39757     /**
39758      * @cfg {String} focusClass @hide
39759      */
39760     /**
39761      * @cfg {String} autoCreate @hide
39762      */
39763     /**
39764      * @cfg {String} inputType @hide
39765      */
39766     /**
39767      * @cfg {String} invalidClass @hide
39768      */
39769     /**
39770      * @cfg {String} invalidText @hide
39771      */
39772     /**
39773      * @cfg {String} msgFx @hide
39774      */
39775     /**
39776      * @cfg {String} validateOnBlur @hide
39777      */
39778 });
39779
39780 Roo.form.HtmlEditor.white = [
39781         'area', 'br', 'img', 'input', 'hr', 'wbr',
39782         
39783        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39784        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39785        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39786        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39787        'table',   'ul',         'xmp', 
39788        
39789        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39790       'thead',   'tr', 
39791      
39792       'dir', 'menu', 'ol', 'ul', 'dl',
39793        
39794       'embed',  'object'
39795 ];
39796
39797
39798 Roo.form.HtmlEditor.black = [
39799     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39800         'applet', // 
39801         'base',   'basefont', 'bgsound', 'blink',  'body', 
39802         'frame',  'frameset', 'head',    'html',   'ilayer', 
39803         'iframe', 'layer',  'link',     'meta',    'object',   
39804         'script', 'style' ,'title',  'xml' // clean later..
39805 ];
39806 Roo.form.HtmlEditor.clean = [
39807     'script', 'style', 'title', 'xml'
39808 ];
39809 Roo.form.HtmlEditor.remove = [
39810     'font'
39811 ];
39812 // attributes..
39813
39814 Roo.form.HtmlEditor.ablack = [
39815     'on'
39816 ];
39817     
39818 Roo.form.HtmlEditor.aclean = [ 
39819     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39820 ];
39821
39822 // protocols..
39823 Roo.form.HtmlEditor.pwhite= [
39824         'http',  'https',  'mailto'
39825 ];
39826
39827 // white listed style attributes.
39828 Roo.form.HtmlEditor.cwhite= [
39829         'text-align',
39830         'font-size'
39831 ];
39832
39833
39834 Roo.form.HtmlEditor.swapCodes   =[ 
39835     [    8211, "--" ], 
39836     [    8212, "--" ], 
39837     [    8216,  "'" ],  
39838     [    8217, "'" ],  
39839     [    8220, '"' ],  
39840     [    8221, '"' ],  
39841     [    8226, "*" ],  
39842     [    8230, "..." ]
39843 ]; 
39844
39845     // <script type="text/javascript">
39846 /*
39847  * Based on
39848  * Ext JS Library 1.1.1
39849  * Copyright(c) 2006-2007, Ext JS, LLC.
39850  *  
39851  
39852  */
39853
39854 /**
39855  * @class Roo.form.HtmlEditorToolbar1
39856  * Basic Toolbar
39857  * 
39858  * Usage:
39859  *
39860  new Roo.form.HtmlEditor({
39861     ....
39862     toolbars : [
39863         new Roo.form.HtmlEditorToolbar1({
39864             disable : { fonts: 1 , format: 1, ..., ... , ...],
39865             btns : [ .... ]
39866         })
39867     }
39868      
39869  * 
39870  * @cfg {Object} disable List of elements to disable..
39871  * @cfg {Array} btns List of additional buttons.
39872  * 
39873  * 
39874  * NEEDS Extra CSS? 
39875  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39876  */
39877  
39878 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39879 {
39880     
39881     Roo.apply(this, config);
39882     
39883     // default disabled, based on 'good practice'..
39884     this.disable = this.disable || {};
39885     Roo.applyIf(this.disable, {
39886         fontSize : true,
39887         colors : true,
39888         specialElements : true
39889     });
39890     
39891     
39892     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39893     // dont call parent... till later.
39894 }
39895
39896 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39897     
39898     tb: false,
39899     
39900     rendered: false,
39901     
39902     editor : false,
39903     /**
39904      * @cfg {Object} disable  List of toolbar elements to disable
39905          
39906      */
39907     disable : false,
39908       /**
39909      * @cfg {Array} fontFamilies An array of available font families
39910      */
39911     fontFamilies : [
39912         'Arial',
39913         'Courier New',
39914         'Tahoma',
39915         'Times New Roman',
39916         'Verdana'
39917     ],
39918     
39919     specialChars : [
39920            "&#169;",
39921           "&#174;",     
39922           "&#8482;",    
39923           "&#163;" ,    
39924          // "&#8212;",    
39925           "&#8230;",    
39926           "&#247;" ,    
39927         //  "&#225;" ,     ?? a acute?
39928            "&#8364;"    , //Euro
39929        //   "&#8220;"    ,
39930         //  "&#8221;"    ,
39931         //  "&#8226;"    ,
39932           "&#176;"  //   , // degrees
39933
39934          // "&#233;"     , // e ecute
39935          // "&#250;"     , // u ecute?
39936     ],
39937     
39938     specialElements : [
39939         {
39940             text: "Insert Table",
39941             xtype: 'MenuItem',
39942             xns : Roo.Menu,
39943             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39944                 
39945         },
39946         {    
39947             text: "Insert Image",
39948             xtype: 'MenuItem',
39949             xns : Roo.Menu,
39950             ihtml : '<img src="about:blank"/>'
39951             
39952         }
39953         
39954          
39955     ],
39956     
39957     
39958     inputElements : [ 
39959             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39960             "input:submit", "input:button", "select", "textarea", "label" ],
39961     formats : [
39962         ["p"] ,  
39963         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39964         ["pre"],[ "code"], 
39965         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39966     ],
39967      /**
39968      * @cfg {String} defaultFont default font to use.
39969      */
39970     defaultFont: 'tahoma',
39971    
39972     fontSelect : false,
39973     
39974     
39975     formatCombo : false,
39976     
39977     init : function(editor)
39978     {
39979         this.editor = editor;
39980         
39981         
39982         var fid = editor.frameId;
39983         var etb = this;
39984         function btn(id, toggle, handler){
39985             var xid = fid + '-'+ id ;
39986             return {
39987                 id : xid,
39988                 cmd : id,
39989                 cls : 'x-btn-icon x-edit-'+id,
39990                 enableToggle:toggle !== false,
39991                 scope: editor, // was editor...
39992                 handler:handler||editor.relayBtnCmd,
39993                 clickEvent:'mousedown',
39994                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39995                 tabIndex:-1
39996             };
39997         }
39998         
39999         
40000         
40001         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40002         this.tb = tb;
40003          // stop form submits
40004         tb.el.on('click', function(e){
40005             e.preventDefault(); // what does this do?
40006         });
40007
40008         if(!this.disable.font && !Roo.isSafari){
40009             /* why no safari for fonts
40010             editor.fontSelect = tb.el.createChild({
40011                 tag:'select',
40012                 tabIndex: -1,
40013                 cls:'x-font-select',
40014                 html: editor.createFontOptions()
40015             });
40016             editor.fontSelect.on('change', function(){
40017                 var font = editor.fontSelect.dom.value;
40018                 editor.relayCmd('fontname', font);
40019                 editor.deferFocus();
40020             }, editor);
40021             tb.add(
40022                 editor.fontSelect.dom,
40023                 '-'
40024             );
40025             */
40026         };
40027         if(!this.disable.formats){
40028             this.formatCombo = new Roo.form.ComboBox({
40029                 store: new Roo.data.SimpleStore({
40030                     id : 'tag',
40031                     fields: ['tag'],
40032                     data : this.formats // from states.js
40033                 }),
40034                 blockFocus : true,
40035                 //autoCreate : {tag: "div",  size: "20"},
40036                 displayField:'tag',
40037                 typeAhead: false,
40038                 mode: 'local',
40039                 editable : false,
40040                 triggerAction: 'all',
40041                 emptyText:'Add tag',
40042                 selectOnFocus:true,
40043                 width:135,
40044                 listeners : {
40045                     'select': function(c, r, i) {
40046                         editor.insertTag(r.get('tag'));
40047                         editor.focus();
40048                     }
40049                 }
40050
40051             });
40052             tb.addField(this.formatCombo);
40053             
40054         }
40055         
40056         if(!this.disable.format){
40057             tb.add(
40058                 btn('bold'),
40059                 btn('italic'),
40060                 btn('underline')
40061             );
40062         };
40063         if(!this.disable.fontSize){
40064             tb.add(
40065                 '-',
40066                 
40067                 
40068                 btn('increasefontsize', false, editor.adjustFont),
40069                 btn('decreasefontsize', false, editor.adjustFont)
40070             );
40071         };
40072         
40073         
40074         if(!this.disable.colors){
40075             tb.add(
40076                 '-', {
40077                     id:editor.frameId +'-forecolor',
40078                     cls:'x-btn-icon x-edit-forecolor',
40079                     clickEvent:'mousedown',
40080                     tooltip: this.buttonTips['forecolor'] || undefined,
40081                     tabIndex:-1,
40082                     menu : new Roo.menu.ColorMenu({
40083                         allowReselect: true,
40084                         focus: Roo.emptyFn,
40085                         value:'000000',
40086                         plain:true,
40087                         selectHandler: function(cp, color){
40088                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40089                             editor.deferFocus();
40090                         },
40091                         scope: editor,
40092                         clickEvent:'mousedown'
40093                     })
40094                 }, {
40095                     id:editor.frameId +'backcolor',
40096                     cls:'x-btn-icon x-edit-backcolor',
40097                     clickEvent:'mousedown',
40098                     tooltip: this.buttonTips['backcolor'] || undefined,
40099                     tabIndex:-1,
40100                     menu : new Roo.menu.ColorMenu({
40101                         focus: Roo.emptyFn,
40102                         value:'FFFFFF',
40103                         plain:true,
40104                         allowReselect: true,
40105                         selectHandler: function(cp, color){
40106                             if(Roo.isGecko){
40107                                 editor.execCmd('useCSS', false);
40108                                 editor.execCmd('hilitecolor', color);
40109                                 editor.execCmd('useCSS', true);
40110                                 editor.deferFocus();
40111                             }else{
40112                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40113                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40114                                 editor.deferFocus();
40115                             }
40116                         },
40117                         scope:editor,
40118                         clickEvent:'mousedown'
40119                     })
40120                 }
40121             );
40122         };
40123         // now add all the items...
40124         
40125
40126         if(!this.disable.alignments){
40127             tb.add(
40128                 '-',
40129                 btn('justifyleft'),
40130                 btn('justifycenter'),
40131                 btn('justifyright')
40132             );
40133         };
40134
40135         //if(!Roo.isSafari){
40136             if(!this.disable.links){
40137                 tb.add(
40138                     '-',
40139                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40140                 );
40141             };
40142
40143             if(!this.disable.lists){
40144                 tb.add(
40145                     '-',
40146                     btn('insertorderedlist'),
40147                     btn('insertunorderedlist')
40148                 );
40149             }
40150             if(!this.disable.sourceEdit){
40151                 tb.add(
40152                     '-',
40153                     btn('sourceedit', true, function(btn){
40154                         this.toggleSourceEdit(btn.pressed);
40155                     })
40156                 );
40157             }
40158         //}
40159         
40160         var smenu = { };
40161         // special menu.. - needs to be tidied up..
40162         if (!this.disable.special) {
40163             smenu = {
40164                 text: "&#169;",
40165                 cls: 'x-edit-none',
40166                 
40167                 menu : {
40168                     items : []
40169                 }
40170             };
40171             for (var i =0; i < this.specialChars.length; i++) {
40172                 smenu.menu.items.push({
40173                     
40174                     html: this.specialChars[i],
40175                     handler: function(a,b) {
40176                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40177                         
40178                     },
40179                     tabIndex:-1
40180                 });
40181             }
40182             
40183             
40184             tb.add(smenu);
40185             
40186             
40187         }
40188          
40189         if (!this.disable.specialElements) {
40190             var semenu = {
40191                 text: "Other;",
40192                 cls: 'x-edit-none',
40193                 menu : {
40194                     items : []
40195                 }
40196             };
40197             for (var i =0; i < this.specialElements.length; i++) {
40198                 semenu.menu.items.push(
40199                     Roo.apply({ 
40200                         handler: function(a,b) {
40201                             editor.insertAtCursor(this.ihtml);
40202                         }
40203                     }, this.specialElements[i])
40204                 );
40205                     
40206             }
40207             
40208             tb.add(semenu);
40209             
40210             
40211         }
40212          
40213         
40214         if (this.btns) {
40215             for(var i =0; i< this.btns.length;i++) {
40216                 var b = this.btns[i];
40217                 b.cls =  'x-edit-none';
40218                 b.scope = editor;
40219                 tb.add(b);
40220             }
40221         
40222         }
40223         
40224         
40225         
40226         // disable everything...
40227         
40228         this.tb.items.each(function(item){
40229            if(item.id != editor.frameId+ '-sourceedit'){
40230                 item.disable();
40231             }
40232         });
40233         this.rendered = true;
40234         
40235         // the all the btns;
40236         editor.on('editorevent', this.updateToolbar, this);
40237         // other toolbars need to implement this..
40238         //editor.on('editmodechange', this.updateToolbar, this);
40239     },
40240     
40241     
40242     
40243     /**
40244      * Protected method that will not generally be called directly. It triggers
40245      * a toolbar update by reading the markup state of the current selection in the editor.
40246      */
40247     updateToolbar: function(){
40248
40249         if(!this.editor.activated){
40250             this.editor.onFirstFocus();
40251             return;
40252         }
40253
40254         var btns = this.tb.items.map, 
40255             doc = this.editor.doc,
40256             frameId = this.editor.frameId;
40257
40258         if(!this.disable.font && !Roo.isSafari){
40259             /*
40260             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40261             if(name != this.fontSelect.dom.value){
40262                 this.fontSelect.dom.value = name;
40263             }
40264             */
40265         }
40266         if(!this.disable.format){
40267             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40268             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40269             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40270         }
40271         if(!this.disable.alignments){
40272             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40273             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40274             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40275         }
40276         if(!Roo.isSafari && !this.disable.lists){
40277             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40278             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40279         }
40280         
40281         var ans = this.editor.getAllAncestors();
40282         if (this.formatCombo) {
40283             
40284             
40285             var store = this.formatCombo.store;
40286             this.formatCombo.setValue("");
40287             for (var i =0; i < ans.length;i++) {
40288                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40289                     // select it..
40290                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40291                     break;
40292                 }
40293             }
40294         }
40295         
40296         
40297         
40298         // hides menus... - so this cant be on a menu...
40299         Roo.menu.MenuMgr.hideAll();
40300
40301         //this.editorsyncValue();
40302     },
40303    
40304     
40305     createFontOptions : function(){
40306         var buf = [], fs = this.fontFamilies, ff, lc;
40307         for(var i = 0, len = fs.length; i< len; i++){
40308             ff = fs[i];
40309             lc = ff.toLowerCase();
40310             buf.push(
40311                 '<option value="',lc,'" style="font-family:',ff,';"',
40312                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40313                     ff,
40314                 '</option>'
40315             );
40316         }
40317         return buf.join('');
40318     },
40319     
40320     toggleSourceEdit : function(sourceEditMode){
40321         if(sourceEditMode === undefined){
40322             sourceEditMode = !this.sourceEditMode;
40323         }
40324         this.sourceEditMode = sourceEditMode === true;
40325         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40326         // just toggle the button?
40327         if(btn.pressed !== this.editor.sourceEditMode){
40328             btn.toggle(this.editor.sourceEditMode);
40329             return;
40330         }
40331         
40332         if(this.sourceEditMode){
40333             this.tb.items.each(function(item){
40334                 if(item.cmd != 'sourceedit'){
40335                     item.disable();
40336                 }
40337             });
40338           
40339         }else{
40340             if(this.initialized){
40341                 this.tb.items.each(function(item){
40342                     item.enable();
40343                 });
40344             }
40345             
40346         }
40347         // tell the editor that it's been pressed..
40348         this.editor.toggleSourceEdit(sourceEditMode);
40349        
40350     },
40351      /**
40352      * Object collection of toolbar tooltips for the buttons in the editor. The key
40353      * is the command id associated with that button and the value is a valid QuickTips object.
40354      * For example:
40355 <pre><code>
40356 {
40357     bold : {
40358         title: 'Bold (Ctrl+B)',
40359         text: 'Make the selected text bold.',
40360         cls: 'x-html-editor-tip'
40361     },
40362     italic : {
40363         title: 'Italic (Ctrl+I)',
40364         text: 'Make the selected text italic.',
40365         cls: 'x-html-editor-tip'
40366     },
40367     ...
40368 </code></pre>
40369     * @type Object
40370      */
40371     buttonTips : {
40372         bold : {
40373             title: 'Bold (Ctrl+B)',
40374             text: 'Make the selected text bold.',
40375             cls: 'x-html-editor-tip'
40376         },
40377         italic : {
40378             title: 'Italic (Ctrl+I)',
40379             text: 'Make the selected text italic.',
40380             cls: 'x-html-editor-tip'
40381         },
40382         underline : {
40383             title: 'Underline (Ctrl+U)',
40384             text: 'Underline the selected text.',
40385             cls: 'x-html-editor-tip'
40386         },
40387         increasefontsize : {
40388             title: 'Grow Text',
40389             text: 'Increase the font size.',
40390             cls: 'x-html-editor-tip'
40391         },
40392         decreasefontsize : {
40393             title: 'Shrink Text',
40394             text: 'Decrease the font size.',
40395             cls: 'x-html-editor-tip'
40396         },
40397         backcolor : {
40398             title: 'Text Highlight Color',
40399             text: 'Change the background color of the selected text.',
40400             cls: 'x-html-editor-tip'
40401         },
40402         forecolor : {
40403             title: 'Font Color',
40404             text: 'Change the color of the selected text.',
40405             cls: 'x-html-editor-tip'
40406         },
40407         justifyleft : {
40408             title: 'Align Text Left',
40409             text: 'Align text to the left.',
40410             cls: 'x-html-editor-tip'
40411         },
40412         justifycenter : {
40413             title: 'Center Text',
40414             text: 'Center text in the editor.',
40415             cls: 'x-html-editor-tip'
40416         },
40417         justifyright : {
40418             title: 'Align Text Right',
40419             text: 'Align text to the right.',
40420             cls: 'x-html-editor-tip'
40421         },
40422         insertunorderedlist : {
40423             title: 'Bullet List',
40424             text: 'Start a bulleted list.',
40425             cls: 'x-html-editor-tip'
40426         },
40427         insertorderedlist : {
40428             title: 'Numbered List',
40429             text: 'Start a numbered list.',
40430             cls: 'x-html-editor-tip'
40431         },
40432         createlink : {
40433             title: 'Hyperlink',
40434             text: 'Make the selected text a hyperlink.',
40435             cls: 'x-html-editor-tip'
40436         },
40437         sourceedit : {
40438             title: 'Source Edit',
40439             text: 'Switch to source editing mode.',
40440             cls: 'x-html-editor-tip'
40441         }
40442     },
40443     // private
40444     onDestroy : function(){
40445         if(this.rendered){
40446             
40447             this.tb.items.each(function(item){
40448                 if(item.menu){
40449                     item.menu.removeAll();
40450                     if(item.menu.el){
40451                         item.menu.el.destroy();
40452                     }
40453                 }
40454                 item.destroy();
40455             });
40456              
40457         }
40458     },
40459     onFirstFocus: function() {
40460         this.tb.items.each(function(item){
40461            item.enable();
40462         });
40463     }
40464 });
40465
40466
40467
40468
40469 // <script type="text/javascript">
40470 /*
40471  * Based on
40472  * Ext JS Library 1.1.1
40473  * Copyright(c) 2006-2007, Ext JS, LLC.
40474  *  
40475  
40476  */
40477
40478  
40479 /**
40480  * @class Roo.form.HtmlEditor.ToolbarContext
40481  * Context Toolbar
40482  * 
40483  * Usage:
40484  *
40485  new Roo.form.HtmlEditor({
40486     ....
40487     toolbars : [
40488         { xtype: 'ToolbarStandard', styles : {} }
40489         { xtype: 'ToolbarContext', disable : {} }
40490     ]
40491 })
40492
40493      
40494  * 
40495  * @config : {Object} disable List of elements to disable.. (not done yet.)
40496  * @config : {Object} styles  Map of styles available.
40497  * 
40498  */
40499
40500 Roo.form.HtmlEditor.ToolbarContext = function(config)
40501 {
40502     
40503     Roo.apply(this, config);
40504     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40505     // dont call parent... till later.
40506     this.styles = this.styles || {};
40507 }
40508 Roo.form.HtmlEditor.ToolbarContext.types = {
40509     'IMG' : {
40510         width : {
40511             title: "Width",
40512             width: 40
40513         },
40514         height:  {
40515             title: "Height",
40516             width: 40
40517         },
40518         align: {
40519             title: "Align",
40520             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40521             width : 80
40522             
40523         },
40524         border: {
40525             title: "Border",
40526             width: 40
40527         },
40528         alt: {
40529             title: "Alt",
40530             width: 120
40531         },
40532         src : {
40533             title: "Src",
40534             width: 220
40535         }
40536         
40537     },
40538     'A' : {
40539         name : {
40540             title: "Name",
40541             width: 50
40542         },
40543         href:  {
40544             title: "Href",
40545             width: 220
40546         } // border?
40547         
40548     },
40549     'TABLE' : {
40550         rows : {
40551             title: "Rows",
40552             width: 20
40553         },
40554         cols : {
40555             title: "Cols",
40556             width: 20
40557         },
40558         width : {
40559             title: "Width",
40560             width: 40
40561         },
40562         height : {
40563             title: "Height",
40564             width: 40
40565         },
40566         border : {
40567             title: "Border",
40568             width: 20
40569         }
40570     },
40571     'TD' : {
40572         width : {
40573             title: "Width",
40574             width: 40
40575         },
40576         height : {
40577             title: "Height",
40578             width: 40
40579         },   
40580         align: {
40581             title: "Align",
40582             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40583             width: 80
40584         },
40585         valign: {
40586             title: "Valign",
40587             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40588             width: 80
40589         },
40590         colspan: {
40591             title: "Colspan",
40592             width: 20
40593             
40594         }
40595     },
40596     'INPUT' : {
40597         name : {
40598             title: "name",
40599             width: 120
40600         },
40601         value : {
40602             title: "Value",
40603             width: 120
40604         },
40605         width : {
40606             title: "Width",
40607             width: 40
40608         }
40609     },
40610     'LABEL' : {
40611         'for' : {
40612             title: "For",
40613             width: 120
40614         }
40615     },
40616     'TEXTAREA' : {
40617           name : {
40618             title: "name",
40619             width: 120
40620         },
40621         rows : {
40622             title: "Rows",
40623             width: 20
40624         },
40625         cols : {
40626             title: "Cols",
40627             width: 20
40628         }
40629     },
40630     'SELECT' : {
40631         name : {
40632             title: "name",
40633             width: 120
40634         },
40635         selectoptions : {
40636             title: "Options",
40637             width: 200
40638         }
40639     },
40640     
40641     // should we really allow this??
40642     // should this just be 
40643     'BODY' : {
40644         title : {
40645             title: "title",
40646             width: 200,
40647             disabled : true
40648         }
40649     },
40650     '*' : {
40651         // empty..
40652     }
40653 };
40654
40655
40656
40657 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40658     
40659     tb: false,
40660     
40661     rendered: false,
40662     
40663     editor : false,
40664     /**
40665      * @cfg {Object} disable  List of toolbar elements to disable
40666          
40667      */
40668     disable : false,
40669     /**
40670      * @cfg {Object} styles List of styles 
40671      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40672      *
40673      * These must be defined in the page, so they get rendered correctly..
40674      * .headline { }
40675      * TD.underline { }
40676      * 
40677      */
40678     styles : false,
40679     
40680     
40681     
40682     toolbars : false,
40683     
40684     init : function(editor)
40685     {
40686         this.editor = editor;
40687         
40688         
40689         var fid = editor.frameId;
40690         var etb = this;
40691         function btn(id, toggle, handler){
40692             var xid = fid + '-'+ id ;
40693             return {
40694                 id : xid,
40695                 cmd : id,
40696                 cls : 'x-btn-icon x-edit-'+id,
40697                 enableToggle:toggle !== false,
40698                 scope: editor, // was editor...
40699                 handler:handler||editor.relayBtnCmd,
40700                 clickEvent:'mousedown',
40701                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40702                 tabIndex:-1
40703             };
40704         }
40705         // create a new element.
40706         var wdiv = editor.wrap.createChild({
40707                 tag: 'div'
40708             }, editor.wrap.dom.firstChild.nextSibling, true);
40709         
40710         // can we do this more than once??
40711         
40712          // stop form submits
40713       
40714  
40715         // disable everything...
40716         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40717         this.toolbars = {};
40718            
40719         for (var i in  ty) {
40720           
40721             this.toolbars[i] = this.buildToolbar(ty[i],i);
40722         }
40723         this.tb = this.toolbars.BODY;
40724         this.tb.el.show();
40725         this.buildFooter();
40726         this.footer.show();
40727          
40728         this.rendered = true;
40729         
40730         // the all the btns;
40731         editor.on('editorevent', this.updateToolbar, this);
40732         // other toolbars need to implement this..
40733         //editor.on('editmodechange', this.updateToolbar, this);
40734     },
40735     
40736     
40737     
40738     /**
40739      * Protected method that will not generally be called directly. It triggers
40740      * a toolbar update by reading the markup state of the current selection in the editor.
40741      */
40742     updateToolbar: function(ignore_a,ignore_b,sel){
40743
40744         
40745         if(!this.editor.activated){
40746              this.editor.onFirstFocus();
40747             return;
40748         }
40749         var updateFooter = sel ? false : true;
40750         
40751         
40752         var ans = this.editor.getAllAncestors();
40753         
40754         // pick
40755         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40756         
40757         if (!sel) { 
40758             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40759             sel = sel ? sel : this.editor.doc.body;
40760             sel = sel.tagName.length ? sel : this.editor.doc.body;
40761             
40762         }
40763         // pick a menu that exists..
40764         var tn = sel.tagName.toUpperCase();
40765         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40766         
40767         tn = sel.tagName.toUpperCase();
40768         
40769         var lastSel = this.tb.selectedNode
40770         
40771         this.tb.selectedNode = sel;
40772         
40773         // if current menu does not match..
40774         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40775                 
40776             this.tb.el.hide();
40777             ///console.log("show: " + tn);
40778             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40779             this.tb.el.show();
40780             // update name
40781             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40782             
40783             
40784             // update attributes
40785             if (this.tb.fields) {
40786                 this.tb.fields.each(function(e) {
40787                    e.setValue(sel.getAttribute(e.name));
40788                 });
40789             }
40790             
40791             // update styles
40792             var st = this.tb.fields.item(0);
40793             st.store.removeAll();
40794             var cn = sel.className.split(/\s+/);
40795             
40796             var avs = [];
40797             if (this.styles['*']) {
40798                 
40799                 Roo.each(this.styles['*'], function(v) {
40800                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40801                 });
40802             }
40803             if (this.styles[tn]) { 
40804                 Roo.each(this.styles[tn], function(v) {
40805                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40806                 });
40807             }
40808             
40809             st.store.loadData(avs);
40810             st.collapse();
40811             st.setValue(cn);
40812             
40813             // flag our selected Node.
40814             this.tb.selectedNode = sel;
40815            
40816            
40817             Roo.menu.MenuMgr.hideAll();
40818
40819         }
40820         
40821         if (!updateFooter) {
40822             return;
40823         }
40824         // update the footer
40825         //
40826         var html = '';
40827         
40828         this.footerEls = ans.reverse();
40829         Roo.each(this.footerEls, function(a,i) {
40830             if (!a) { return; }
40831             html += html.length ? ' &gt; '  :  '';
40832             
40833             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40834             
40835         });
40836        
40837         // 
40838         var sz = this.footDisp.up('td').getSize();
40839         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40840         this.footDisp.dom.style.marginLeft = '5px';
40841         
40842         this.footDisp.dom.style.overflow = 'hidden';
40843         
40844         this.footDisp.dom.innerHTML = html;
40845             
40846         //this.editorsyncValue();
40847     },
40848    
40849        
40850     // private
40851     onDestroy : function(){
40852         if(this.rendered){
40853             
40854             this.tb.items.each(function(item){
40855                 if(item.menu){
40856                     item.menu.removeAll();
40857                     if(item.menu.el){
40858                         item.menu.el.destroy();
40859                     }
40860                 }
40861                 item.destroy();
40862             });
40863              
40864         }
40865     },
40866     onFirstFocus: function() {
40867         // need to do this for all the toolbars..
40868         this.tb.items.each(function(item){
40869            item.enable();
40870         });
40871     },
40872     buildToolbar: function(tlist, nm)
40873     {
40874         var editor = this.editor;
40875          // create a new element.
40876         var wdiv = editor.wrap.createChild({
40877                 tag: 'div'
40878             }, editor.wrap.dom.firstChild.nextSibling, true);
40879         
40880        
40881         var tb = new Roo.Toolbar(wdiv);
40882         // add the name..
40883         
40884         tb.add(nm+ ":&nbsp;");
40885         
40886         // styles...
40887         if (this.styles) {
40888             
40889             // this needs a multi-select checkbox...
40890             tb.addField( new Roo.form.ComboBox({
40891                 store: new Roo.data.SimpleStore({
40892                     id : 'val',
40893                     fields: ['val', 'selected'],
40894                     data : [] 
40895                 }),
40896                 name : 'className',
40897                 displayField:'val',
40898                 typeAhead: false,
40899                 mode: 'local',
40900                 editable : false,
40901                 triggerAction: 'all',
40902                 emptyText:'Select Style',
40903                 selectOnFocus:true,
40904                 width: 130,
40905                 listeners : {
40906                     'select': function(c, r, i) {
40907                         // initial support only for on class per el..
40908                         tb.selectedNode.className =  r ? r.get('val') : '';
40909                     }
40910                 }
40911     
40912             }));
40913         }
40914             
40915         
40916         
40917         for (var i in tlist) {
40918             
40919             var item = tlist[i];
40920             tb.add(item.title + ":&nbsp;");
40921             
40922             
40923             
40924             
40925             if (item.opts) {
40926                 // opts == pulldown..
40927                 tb.addField( new Roo.form.ComboBox({
40928                     store: new Roo.data.SimpleStore({
40929                         id : 'val',
40930                         fields: ['val'],
40931                         data : item.opts  
40932                     }),
40933                     name : i,
40934                     displayField:'val',
40935                     typeAhead: false,
40936                     mode: 'local',
40937                     editable : false,
40938                     triggerAction: 'all',
40939                     emptyText:'Select',
40940                     selectOnFocus:true,
40941                     width: item.width ? item.width  : 130,
40942                     listeners : {
40943                         'select': function(c, r, i) {
40944                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40945                         }
40946                     }
40947
40948                 }));
40949                 continue;
40950                     
40951                  
40952                 
40953                 tb.addField( new Roo.form.TextField({
40954                     name: i,
40955                     width: 100,
40956                     //allowBlank:false,
40957                     value: ''
40958                 }));
40959                 continue;
40960             }
40961             tb.addField( new Roo.form.TextField({
40962                 name: i,
40963                 width: item.width,
40964                 //allowBlank:true,
40965                 value: '',
40966                 listeners: {
40967                     'change' : function(f, nv, ov) {
40968                         tb.selectedNode.setAttribute(f.name, nv);
40969                     }
40970                 }
40971             }));
40972              
40973         }
40974         tb.el.on('click', function(e){
40975             e.preventDefault(); // what does this do?
40976         });
40977         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40978         tb.el.hide();
40979         tb.name = nm;
40980         // dont need to disable them... as they will get hidden
40981         return tb;
40982          
40983         
40984     },
40985     buildFooter : function()
40986     {
40987         
40988         var fel = this.editor.wrap.createChild();
40989         this.footer = new Roo.Toolbar(fel);
40990         // toolbar has scrolly on left / right?
40991         var footDisp= new Roo.Toolbar.Fill();
40992         var _t = this;
40993         this.footer.add(
40994             {
40995                 text : '&lt;',
40996                 xtype: 'Button',
40997                 handler : function() {
40998                     _t.footDisp.scrollTo('left',0,true)
40999                 }
41000             }
41001         );
41002         this.footer.add( footDisp );
41003         this.footer.add( 
41004             {
41005                 text : '&gt;',
41006                 xtype: 'Button',
41007                 handler : function() {
41008                     // no animation..
41009                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41010                 }
41011             }
41012         );
41013         var fel = Roo.get(footDisp.el);
41014         fel.addClass('x-editor-context');
41015         this.footDispWrap = fel; 
41016         this.footDispWrap.overflow  = 'hidden';
41017         
41018         this.footDisp = fel.createChild();
41019         this.footDispWrap.on('click', this.onContextClick, this)
41020         
41021         
41022     },
41023     onContextClick : function (ev,dom)
41024     {
41025         ev.preventDefault();
41026         var  cn = dom.className;
41027         Roo.log(cn);
41028         if (!cn.match(/x-ed-loc-/)) {
41029             return;
41030         }
41031         var n = cn.split('-').pop();
41032         var ans = this.footerEls;
41033         var sel = ans[n];
41034         
41035          // pick
41036         var range = this.editor.createRange();
41037         
41038         range.selectNodeContents(sel);
41039         //range.selectNode(sel);
41040         
41041         
41042         var selection = this.editor.getSelection();
41043         selection.removeAllRanges();
41044         selection.addRange(range);
41045         
41046         
41047         
41048         this.updateToolbar(null, null, sel);
41049         
41050         
41051     }
41052     
41053     
41054     
41055     
41056     
41057 });
41058
41059
41060
41061
41062
41063 /*
41064  * Based on:
41065  * Ext JS Library 1.1.1
41066  * Copyright(c) 2006-2007, Ext JS, LLC.
41067  *
41068  * Originally Released Under LGPL - original licence link has changed is not relivant.
41069  *
41070  * Fork - LGPL
41071  * <script type="text/javascript">
41072  */
41073  
41074 /**
41075  * @class Roo.form.BasicForm
41076  * @extends Roo.util.Observable
41077  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41078  * @constructor
41079  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41080  * @param {Object} config Configuration options
41081  */
41082 Roo.form.BasicForm = function(el, config){
41083     this.allItems = [];
41084     this.childForms = [];
41085     Roo.apply(this, config);
41086     /*
41087      * The Roo.form.Field items in this form.
41088      * @type MixedCollection
41089      */
41090      
41091      
41092     this.items = new Roo.util.MixedCollection(false, function(o){
41093         return o.id || (o.id = Roo.id());
41094     });
41095     this.addEvents({
41096         /**
41097          * @event beforeaction
41098          * Fires before any action is performed. Return false to cancel the action.
41099          * @param {Form} this
41100          * @param {Action} action The action to be performed
41101          */
41102         beforeaction: true,
41103         /**
41104          * @event actionfailed
41105          * Fires when an action fails.
41106          * @param {Form} this
41107          * @param {Action} action The action that failed
41108          */
41109         actionfailed : true,
41110         /**
41111          * @event actioncomplete
41112          * Fires when an action is completed.
41113          * @param {Form} this
41114          * @param {Action} action The action that completed
41115          */
41116         actioncomplete : true
41117     });
41118     if(el){
41119         this.initEl(el);
41120     }
41121     Roo.form.BasicForm.superclass.constructor.call(this);
41122 };
41123
41124 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41125     /**
41126      * @cfg {String} method
41127      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41128      */
41129     /**
41130      * @cfg {DataReader} reader
41131      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41132      * This is optional as there is built-in support for processing JSON.
41133      */
41134     /**
41135      * @cfg {DataReader} errorReader
41136      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41137      * This is completely optional as there is built-in support for processing JSON.
41138      */
41139     /**
41140      * @cfg {String} url
41141      * The URL to use for form actions if one isn't supplied in the action options.
41142      */
41143     /**
41144      * @cfg {Boolean} fileUpload
41145      * Set to true if this form is a file upload.
41146      */
41147      
41148     /**
41149      * @cfg {Object} baseParams
41150      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41151      */
41152      /**
41153      
41154     /**
41155      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41156      */
41157     timeout: 30,
41158
41159     // private
41160     activeAction : null,
41161
41162     /**
41163      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41164      * or setValues() data instead of when the form was first created.
41165      */
41166     trackResetOnLoad : false,
41167     
41168     
41169     /**
41170      * childForms - used for multi-tab forms
41171      * @type {Array}
41172      */
41173     childForms : false,
41174     
41175     /**
41176      * allItems - full list of fields.
41177      * @type {Array}
41178      */
41179     allItems : false,
41180     
41181     /**
41182      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41183      * element by passing it or its id or mask the form itself by passing in true.
41184      * @type Mixed
41185      */
41186     waitMsgTarget : false,
41187
41188     // private
41189     initEl : function(el){
41190         this.el = Roo.get(el);
41191         this.id = this.el.id || Roo.id();
41192         this.el.on('submit', this.onSubmit, this);
41193         this.el.addClass('x-form');
41194     },
41195
41196     // private
41197     onSubmit : function(e){
41198         e.stopEvent();
41199     },
41200
41201     /**
41202      * Returns true if client-side validation on the form is successful.
41203      * @return Boolean
41204      */
41205     isValid : function(){
41206         var valid = true;
41207         this.items.each(function(f){
41208            if(!f.validate()){
41209                valid = false;
41210            }
41211         });
41212         return valid;
41213     },
41214
41215     /**
41216      * Returns true if any fields in this form have changed since their original load.
41217      * @return Boolean
41218      */
41219     isDirty : function(){
41220         var dirty = false;
41221         this.items.each(function(f){
41222            if(f.isDirty()){
41223                dirty = true;
41224                return false;
41225            }
41226         });
41227         return dirty;
41228     },
41229
41230     /**
41231      * Performs a predefined action (submit or load) or custom actions you define on this form.
41232      * @param {String} actionName The name of the action type
41233      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41234      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41235      * accept other config options):
41236      * <pre>
41237 Property          Type             Description
41238 ----------------  ---------------  ----------------------------------------------------------------------------------
41239 url               String           The url for the action (defaults to the form's url)
41240 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41241 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41242 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41243                                    validate the form on the client (defaults to false)
41244      * </pre>
41245      * @return {BasicForm} this
41246      */
41247     doAction : function(action, options){
41248         if(typeof action == 'string'){
41249             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41250         }
41251         if(this.fireEvent('beforeaction', this, action) !== false){
41252             this.beforeAction(action);
41253             action.run.defer(100, action);
41254         }
41255         return this;
41256     },
41257
41258     /**
41259      * Shortcut to do a submit action.
41260      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41261      * @return {BasicForm} this
41262      */
41263     submit : function(options){
41264         this.doAction('submit', options);
41265         return this;
41266     },
41267
41268     /**
41269      * Shortcut to do a load action.
41270      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41271      * @return {BasicForm} this
41272      */
41273     load : function(options){
41274         this.doAction('load', options);
41275         return this;
41276     },
41277
41278     /**
41279      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41280      * @param {Record} record The record to edit
41281      * @return {BasicForm} this
41282      */
41283     updateRecord : function(record){
41284         record.beginEdit();
41285         var fs = record.fields;
41286         fs.each(function(f){
41287             var field = this.findField(f.name);
41288             if(field){
41289                 record.set(f.name, field.getValue());
41290             }
41291         }, this);
41292         record.endEdit();
41293         return this;
41294     },
41295
41296     /**
41297      * Loads an Roo.data.Record into this form.
41298      * @param {Record} record The record to load
41299      * @return {BasicForm} this
41300      */
41301     loadRecord : function(record){
41302         this.setValues(record.data);
41303         return this;
41304     },
41305
41306     // private
41307     beforeAction : function(action){
41308         var o = action.options;
41309         
41310        
41311         if(this.waitMsgTarget === true){
41312             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41313         }else if(this.waitMsgTarget){
41314             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41315             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41316         }else {
41317             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41318         }
41319          
41320     },
41321
41322     // private
41323     afterAction : function(action, success){
41324         this.activeAction = null;
41325         var o = action.options;
41326         
41327         if(this.waitMsgTarget === true){
41328             this.el.unmask();
41329         }else if(this.waitMsgTarget){
41330             this.waitMsgTarget.unmask();
41331         }else{
41332             Roo.MessageBox.updateProgress(1);
41333             Roo.MessageBox.hide();
41334         }
41335          
41336         if(success){
41337             if(o.reset){
41338                 this.reset();
41339             }
41340             Roo.callback(o.success, o.scope, [this, action]);
41341             this.fireEvent('actioncomplete', this, action);
41342             
41343         }else{
41344             Roo.callback(o.failure, o.scope, [this, action]);
41345             // show an error message if no failed handler is set..
41346             if (!this.hasListener('actionfailed')) {
41347                 Roo.MessageBox.alert("Error",
41348                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41349                         action.result.errorMsg :
41350                         "Saving Failed, please check your entries"
41351                 );
41352             }
41353             
41354             this.fireEvent('actionfailed', this, action);
41355         }
41356         
41357     },
41358
41359     /**
41360      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41361      * @param {String} id The value to search for
41362      * @return Field
41363      */
41364     findField : function(id){
41365         var field = this.items.get(id);
41366         if(!field){
41367             this.items.each(function(f){
41368                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41369                     field = f;
41370                     return false;
41371                 }
41372             });
41373         }
41374         return field || null;
41375     },
41376
41377     /**
41378      * Add a secondary form to this one, 
41379      * Used to provide tabbed forms. One form is primary, with hidden values 
41380      * which mirror the elements from the other forms.
41381      * 
41382      * @param {Roo.form.Form} form to add.
41383      * 
41384      */
41385     addForm : function(form)
41386     {
41387        
41388         if (this.childForms.indexOf(form) > -1) {
41389             // already added..
41390             return;
41391         }
41392         this.childForms.push(form);
41393         var n = '';
41394         Roo.each(form.allItems, function (fe) {
41395             
41396             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41397             if (this.findField(n)) { // already added..
41398                 return;
41399             }
41400             var add = new Roo.form.Hidden({
41401                 name : n
41402             });
41403             add.render(this.el);
41404             
41405             this.add( add );
41406         }, this);
41407         
41408     },
41409     /**
41410      * Mark fields in this form invalid in bulk.
41411      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41412      * @return {BasicForm} this
41413      */
41414     markInvalid : function(errors){
41415         if(errors instanceof Array){
41416             for(var i = 0, len = errors.length; i < len; i++){
41417                 var fieldError = errors[i];
41418                 var f = this.findField(fieldError.id);
41419                 if(f){
41420                     f.markInvalid(fieldError.msg);
41421                 }
41422             }
41423         }else{
41424             var field, id;
41425             for(id in errors){
41426                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41427                     field.markInvalid(errors[id]);
41428                 }
41429             }
41430         }
41431         Roo.each(this.childForms || [], function (f) {
41432             f.markInvalid(errors);
41433         });
41434         
41435         return this;
41436     },
41437
41438     /**
41439      * Set values for fields in this form in bulk.
41440      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41441      * @return {BasicForm} this
41442      */
41443     setValues : function(values){
41444         if(values instanceof Array){ // array of objects
41445             for(var i = 0, len = values.length; i < len; i++){
41446                 var v = values[i];
41447                 var f = this.findField(v.id);
41448                 if(f){
41449                     f.setValue(v.value);
41450                     if(this.trackResetOnLoad){
41451                         f.originalValue = f.getValue();
41452                     }
41453                 }
41454             }
41455         }else{ // object hash
41456             var field, id;
41457             for(id in values){
41458                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41459                     
41460                     if (field.setFromData && 
41461                         field.valueField && 
41462                         field.displayField &&
41463                         // combos' with local stores can 
41464                         // be queried via setValue()
41465                         // to set their value..
41466                         (field.store && !field.store.isLocal)
41467                         ) {
41468                         // it's a combo
41469                         var sd = { };
41470                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41471                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41472                         field.setFromData(sd);
41473                         
41474                     } else {
41475                         field.setValue(values[id]);
41476                     }
41477                     
41478                     
41479                     if(this.trackResetOnLoad){
41480                         field.originalValue = field.getValue();
41481                     }
41482                 }
41483             }
41484         }
41485          
41486         Roo.each(this.childForms || [], function (f) {
41487             f.setValues(values);
41488         });
41489                 
41490         return this;
41491     },
41492
41493     /**
41494      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41495      * they are returned as an array.
41496      * @param {Boolean} asString
41497      * @return {Object}
41498      */
41499     getValues : function(asString){
41500         if (this.childForms) {
41501             // copy values from the child forms
41502             Roo.each(this.childForms, function (f) {
41503                 this.setValues(f.getValues());
41504             }, this);
41505         }
41506         
41507         
41508         
41509         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41510         if(asString === true){
41511             return fs;
41512         }
41513         return Roo.urlDecode(fs);
41514     },
41515     
41516     /**
41517      * Returns the fields in this form as an object with key/value pairs. 
41518      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41519      * @return {Object}
41520      */
41521     getFieldValues : function(with_hidden)
41522     {
41523         if (this.childForms) {
41524             // copy values from the child forms
41525             // should this call getFieldValues - probably not as we do not currently copy
41526             // hidden fields when we generate..
41527             Roo.each(this.childForms, function (f) {
41528                 this.setValues(f.getValues());
41529             }, this);
41530         }
41531         
41532         var ret = {};
41533         this.items.each(function(f){
41534             if (!f.getName()) {
41535                 return;
41536             }
41537             var v = f.getValue();
41538             // not sure if this supported any more..
41539             if ((typeof(v) == 'object') && f.getRawValue) {
41540                 v = f.getRawValue() ; // dates..
41541             }
41542             // combo boxes where name != hiddenName...
41543             if (f.name != f.getName()) {
41544                 ret[f.name] = f.getRawValue();
41545             }
41546             ret[f.getName()] = v;
41547         });
41548         
41549         return ret;
41550     },
41551
41552     /**
41553      * Clears all invalid messages in this form.
41554      * @return {BasicForm} this
41555      */
41556     clearInvalid : function(){
41557         this.items.each(function(f){
41558            f.clearInvalid();
41559         });
41560         
41561         Roo.each(this.childForms || [], function (f) {
41562             f.clearInvalid();
41563         });
41564         
41565         
41566         return this;
41567     },
41568
41569     /**
41570      * Resets this form.
41571      * @return {BasicForm} this
41572      */
41573     reset : function(){
41574         this.items.each(function(f){
41575             f.reset();
41576         });
41577         
41578         Roo.each(this.childForms || [], function (f) {
41579             f.reset();
41580         });
41581        
41582         
41583         return this;
41584     },
41585
41586     /**
41587      * Add Roo.form components to this form.
41588      * @param {Field} field1
41589      * @param {Field} field2 (optional)
41590      * @param {Field} etc (optional)
41591      * @return {BasicForm} this
41592      */
41593     add : function(){
41594         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41595         return this;
41596     },
41597
41598
41599     /**
41600      * Removes a field from the items collection (does NOT remove its markup).
41601      * @param {Field} field
41602      * @return {BasicForm} this
41603      */
41604     remove : function(field){
41605         this.items.remove(field);
41606         return this;
41607     },
41608
41609     /**
41610      * Looks at the fields in this form, checks them for an id attribute,
41611      * and calls applyTo on the existing dom element with that id.
41612      * @return {BasicForm} this
41613      */
41614     render : function(){
41615         this.items.each(function(f){
41616             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41617                 f.applyTo(f.id);
41618             }
41619         });
41620         return this;
41621     },
41622
41623     /**
41624      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41625      * @param {Object} values
41626      * @return {BasicForm} this
41627      */
41628     applyToFields : function(o){
41629         this.items.each(function(f){
41630            Roo.apply(f, o);
41631         });
41632         return this;
41633     },
41634
41635     /**
41636      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41637      * @param {Object} values
41638      * @return {BasicForm} this
41639      */
41640     applyIfToFields : function(o){
41641         this.items.each(function(f){
41642            Roo.applyIf(f, o);
41643         });
41644         return this;
41645     }
41646 });
41647
41648 // back compat
41649 Roo.BasicForm = Roo.form.BasicForm;/*
41650  * Based on:
41651  * Ext JS Library 1.1.1
41652  * Copyright(c) 2006-2007, Ext JS, LLC.
41653  *
41654  * Originally Released Under LGPL - original licence link has changed is not relivant.
41655  *
41656  * Fork - LGPL
41657  * <script type="text/javascript">
41658  */
41659
41660 /**
41661  * @class Roo.form.Form
41662  * @extends Roo.form.BasicForm
41663  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41664  * @constructor
41665  * @param {Object} config Configuration options
41666  */
41667 Roo.form.Form = function(config){
41668     var xitems =  [];
41669     if (config.items) {
41670         xitems = config.items;
41671         delete config.items;
41672     }
41673    
41674     
41675     Roo.form.Form.superclass.constructor.call(this, null, config);
41676     this.url = this.url || this.action;
41677     if(!this.root){
41678         this.root = new Roo.form.Layout(Roo.applyIf({
41679             id: Roo.id()
41680         }, config));
41681     }
41682     this.active = this.root;
41683     /**
41684      * Array of all the buttons that have been added to this form via {@link addButton}
41685      * @type Array
41686      */
41687     this.buttons = [];
41688     this.allItems = [];
41689     this.addEvents({
41690         /**
41691          * @event clientvalidation
41692          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41693          * @param {Form} this
41694          * @param {Boolean} valid true if the form has passed client-side validation
41695          */
41696         clientvalidation: true,
41697         /**
41698          * @event rendered
41699          * Fires when the form is rendered
41700          * @param {Roo.form.Form} form
41701          */
41702         rendered : true
41703     });
41704     
41705     if (this.progressUrl) {
41706             // push a hidden field onto the list of fields..
41707             this.addxtype( {
41708                     xns: Roo.form, 
41709                     xtype : 'Hidden', 
41710                     name : 'UPLOAD_IDENTIFIER' 
41711             });
41712         }
41713         
41714     
41715     Roo.each(xitems, this.addxtype, this);
41716     
41717     
41718     
41719 };
41720
41721 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41722     /**
41723      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41724      */
41725     /**
41726      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41727      */
41728     /**
41729      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41730      */
41731     buttonAlign:'center',
41732
41733     /**
41734      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41735      */
41736     minButtonWidth:75,
41737
41738     /**
41739      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41740      * This property cascades to child containers if not set.
41741      */
41742     labelAlign:'left',
41743
41744     /**
41745      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41746      * fires a looping event with that state. This is required to bind buttons to the valid
41747      * state using the config value formBind:true on the button.
41748      */
41749     monitorValid : false,
41750
41751     /**
41752      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41753      */
41754     monitorPoll : 200,
41755     
41756     /**
41757      * @cfg {String} progressUrl - Url to return progress data 
41758      */
41759     
41760     progressUrl : false,
41761   
41762     /**
41763      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41764      * fields are added and the column is closed. If no fields are passed the column remains open
41765      * until end() is called.
41766      * @param {Object} config The config to pass to the column
41767      * @param {Field} field1 (optional)
41768      * @param {Field} field2 (optional)
41769      * @param {Field} etc (optional)
41770      * @return Column The column container object
41771      */
41772     column : function(c){
41773         var col = new Roo.form.Column(c);
41774         this.start(col);
41775         if(arguments.length > 1){ // duplicate code required because of Opera
41776             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41777             this.end();
41778         }
41779         return col;
41780     },
41781
41782     /**
41783      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41784      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41785      * until end() is called.
41786      * @param {Object} config The config to pass to the fieldset
41787      * @param {Field} field1 (optional)
41788      * @param {Field} field2 (optional)
41789      * @param {Field} etc (optional)
41790      * @return FieldSet The fieldset container object
41791      */
41792     fieldset : function(c){
41793         var fs = new Roo.form.FieldSet(c);
41794         this.start(fs);
41795         if(arguments.length > 1){ // duplicate code required because of Opera
41796             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41797             this.end();
41798         }
41799         return fs;
41800     },
41801
41802     /**
41803      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41804      * fields are added and the container is closed. If no fields are passed the container remains open
41805      * until end() is called.
41806      * @param {Object} config The config to pass to the Layout
41807      * @param {Field} field1 (optional)
41808      * @param {Field} field2 (optional)
41809      * @param {Field} etc (optional)
41810      * @return Layout The container object
41811      */
41812     container : function(c){
41813         var l = new Roo.form.Layout(c);
41814         this.start(l);
41815         if(arguments.length > 1){ // duplicate code required because of Opera
41816             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41817             this.end();
41818         }
41819         return l;
41820     },
41821
41822     /**
41823      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41824      * @param {Object} container A Roo.form.Layout or subclass of Layout
41825      * @return {Form} this
41826      */
41827     start : function(c){
41828         // cascade label info
41829         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41830         this.active.stack.push(c);
41831         c.ownerCt = this.active;
41832         this.active = c;
41833         return this;
41834     },
41835
41836     /**
41837      * Closes the current open container
41838      * @return {Form} this
41839      */
41840     end : function(){
41841         if(this.active == this.root){
41842             return this;
41843         }
41844         this.active = this.active.ownerCt;
41845         return this;
41846     },
41847
41848     /**
41849      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41850      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41851      * as the label of the field.
41852      * @param {Field} field1
41853      * @param {Field} field2 (optional)
41854      * @param {Field} etc. (optional)
41855      * @return {Form} this
41856      */
41857     add : function(){
41858         this.active.stack.push.apply(this.active.stack, arguments);
41859         this.allItems.push.apply(this.allItems,arguments);
41860         var r = [];
41861         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41862             if(a[i].isFormField){
41863                 r.push(a[i]);
41864             }
41865         }
41866         if(r.length > 0){
41867             Roo.form.Form.superclass.add.apply(this, r);
41868         }
41869         return this;
41870     },
41871     
41872
41873     
41874     
41875     
41876      /**
41877      * Find any element that has been added to a form, using it's ID or name
41878      * This can include framesets, columns etc. along with regular fields..
41879      * @param {String} id - id or name to find.
41880      
41881      * @return {Element} e - or false if nothing found.
41882      */
41883     findbyId : function(id)
41884     {
41885         var ret = false;
41886         if (!id) {
41887             return ret;
41888         }
41889         Roo.each(this.allItems, function(f){
41890             if (f.id == id || f.name == id ){
41891                 ret = f;
41892                 return false;
41893             }
41894         });
41895         return ret;
41896     },
41897
41898     
41899     
41900     /**
41901      * Render this form into the passed container. This should only be called once!
41902      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41903      * @return {Form} this
41904      */
41905     render : function(ct)
41906     {
41907         
41908         
41909         
41910         ct = Roo.get(ct);
41911         var o = this.autoCreate || {
41912             tag: 'form',
41913             method : this.method || 'POST',
41914             id : this.id || Roo.id()
41915         };
41916         this.initEl(ct.createChild(o));
41917
41918         this.root.render(this.el);
41919         
41920        
41921              
41922         this.items.each(function(f){
41923             f.render('x-form-el-'+f.id);
41924         });
41925
41926         if(this.buttons.length > 0){
41927             // tables are required to maintain order and for correct IE layout
41928             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41929                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41930                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41931             }}, null, true);
41932             var tr = tb.getElementsByTagName('tr')[0];
41933             for(var i = 0, len = this.buttons.length; i < len; i++) {
41934                 var b = this.buttons[i];
41935                 var td = document.createElement('td');
41936                 td.className = 'x-form-btn-td';
41937                 b.render(tr.appendChild(td));
41938             }
41939         }
41940         if(this.monitorValid){ // initialize after render
41941             this.startMonitoring();
41942         }
41943         this.fireEvent('rendered', this);
41944         return this;
41945     },
41946
41947     /**
41948      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41949      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41950      * object or a valid Roo.DomHelper element config
41951      * @param {Function} handler The function called when the button is clicked
41952      * @param {Object} scope (optional) The scope of the handler function
41953      * @return {Roo.Button}
41954      */
41955     addButton : function(config, handler, scope){
41956         var bc = {
41957             handler: handler,
41958             scope: scope,
41959             minWidth: this.minButtonWidth,
41960             hideParent:true
41961         };
41962         if(typeof config == "string"){
41963             bc.text = config;
41964         }else{
41965             Roo.apply(bc, config);
41966         }
41967         var btn = new Roo.Button(null, bc);
41968         this.buttons.push(btn);
41969         return btn;
41970     },
41971
41972      /**
41973      * Adds a series of form elements (using the xtype property as the factory method.
41974      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41975      * @param {Object} config 
41976      */
41977     
41978     addxtype : function()
41979     {
41980         var ar = Array.prototype.slice.call(arguments, 0);
41981         var ret = false;
41982         for(var i = 0; i < ar.length; i++) {
41983             if (!ar[i]) {
41984                 continue; // skip -- if this happends something invalid got sent, we 
41985                 // should ignore it, as basically that interface element will not show up
41986                 // and that should be pretty obvious!!
41987             }
41988             
41989             if (Roo.form[ar[i].xtype]) {
41990                 ar[i].form = this;
41991                 var fe = Roo.factory(ar[i], Roo.form);
41992                 if (!ret) {
41993                     ret = fe;
41994                 }
41995                 fe.form = this;
41996                 if (fe.store) {
41997                     fe.store.form = this;
41998                 }
41999                 if (fe.isLayout) {  
42000                          
42001                     this.start(fe);
42002                     this.allItems.push(fe);
42003                     if (fe.items && fe.addxtype) {
42004                         fe.addxtype.apply(fe, fe.items);
42005                         delete fe.items;
42006                     }
42007                      this.end();
42008                     continue;
42009                 }
42010                 
42011                 
42012                  
42013                 this.add(fe);
42014               //  console.log('adding ' + ar[i].xtype);
42015             }
42016             if (ar[i].xtype == 'Button') {  
42017                 //console.log('adding button');
42018                 //console.log(ar[i]);
42019                 this.addButton(ar[i]);
42020                 this.allItems.push(fe);
42021                 continue;
42022             }
42023             
42024             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42025                 alert('end is not supported on xtype any more, use items');
42026             //    this.end();
42027             //    //console.log('adding end');
42028             }
42029             
42030         }
42031         return ret;
42032     },
42033     
42034     /**
42035      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42036      * option "monitorValid"
42037      */
42038     startMonitoring : function(){
42039         if(!this.bound){
42040             this.bound = true;
42041             Roo.TaskMgr.start({
42042                 run : this.bindHandler,
42043                 interval : this.monitorPoll || 200,
42044                 scope: this
42045             });
42046         }
42047     },
42048
42049     /**
42050      * Stops monitoring of the valid state of this form
42051      */
42052     stopMonitoring : function(){
42053         this.bound = false;
42054     },
42055
42056     // private
42057     bindHandler : function(){
42058         if(!this.bound){
42059             return false; // stops binding
42060         }
42061         var valid = true;
42062         this.items.each(function(f){
42063             if(!f.isValid(true)){
42064                 valid = false;
42065                 return false;
42066             }
42067         });
42068         for(var i = 0, len = this.buttons.length; i < len; i++){
42069             var btn = this.buttons[i];
42070             if(btn.formBind === true && btn.disabled === valid){
42071                 btn.setDisabled(!valid);
42072             }
42073         }
42074         this.fireEvent('clientvalidation', this, valid);
42075     }
42076     
42077     
42078     
42079     
42080     
42081     
42082     
42083     
42084 });
42085
42086
42087 // back compat
42088 Roo.Form = Roo.form.Form;
42089 /*
42090  * Based on:
42091  * Ext JS Library 1.1.1
42092  * Copyright(c) 2006-2007, Ext JS, LLC.
42093  *
42094  * Originally Released Under LGPL - original licence link has changed is not relivant.
42095  *
42096  * Fork - LGPL
42097  * <script type="text/javascript">
42098  */
42099  
42100  /**
42101  * @class Roo.form.Action
42102  * Internal Class used to handle form actions
42103  * @constructor
42104  * @param {Roo.form.BasicForm} el The form element or its id
42105  * @param {Object} config Configuration options
42106  */
42107  
42108  
42109 // define the action interface
42110 Roo.form.Action = function(form, options){
42111     this.form = form;
42112     this.options = options || {};
42113 };
42114 /**
42115  * Client Validation Failed
42116  * @const 
42117  */
42118 Roo.form.Action.CLIENT_INVALID = 'client';
42119 /**
42120  * Server Validation Failed
42121  * @const 
42122  */
42123  Roo.form.Action.SERVER_INVALID = 'server';
42124  /**
42125  * Connect to Server Failed
42126  * @const 
42127  */
42128 Roo.form.Action.CONNECT_FAILURE = 'connect';
42129 /**
42130  * Reading Data from Server Failed
42131  * @const 
42132  */
42133 Roo.form.Action.LOAD_FAILURE = 'load';
42134
42135 Roo.form.Action.prototype = {
42136     type : 'default',
42137     failureType : undefined,
42138     response : undefined,
42139     result : undefined,
42140
42141     // interface method
42142     run : function(options){
42143
42144     },
42145
42146     // interface method
42147     success : function(response){
42148
42149     },
42150
42151     // interface method
42152     handleResponse : function(response){
42153
42154     },
42155
42156     // default connection failure
42157     failure : function(response){
42158         
42159         this.response = response;
42160         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42161         this.form.afterAction(this, false);
42162     },
42163
42164     processResponse : function(response){
42165         this.response = response;
42166         if(!response.responseText){
42167             return true;
42168         }
42169         this.result = this.handleResponse(response);
42170         return this.result;
42171     },
42172
42173     // utility functions used internally
42174     getUrl : function(appendParams){
42175         var url = this.options.url || this.form.url || this.form.el.dom.action;
42176         if(appendParams){
42177             var p = this.getParams();
42178             if(p){
42179                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42180             }
42181         }
42182         return url;
42183     },
42184
42185     getMethod : function(){
42186         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42187     },
42188
42189     getParams : function(){
42190         var bp = this.form.baseParams;
42191         var p = this.options.params;
42192         if(p){
42193             if(typeof p == "object"){
42194                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42195             }else if(typeof p == 'string' && bp){
42196                 p += '&' + Roo.urlEncode(bp);
42197             }
42198         }else if(bp){
42199             p = Roo.urlEncode(bp);
42200         }
42201         return p;
42202     },
42203
42204     createCallback : function(){
42205         return {
42206             success: this.success,
42207             failure: this.failure,
42208             scope: this,
42209             timeout: (this.form.timeout*1000),
42210             upload: this.form.fileUpload ? this.success : undefined
42211         };
42212     }
42213 };
42214
42215 Roo.form.Action.Submit = function(form, options){
42216     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42217 };
42218
42219 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42220     type : 'submit',
42221
42222     haveProgress : false,
42223     uploadComplete : false,
42224     
42225     // uploadProgress indicator.
42226     uploadProgress : function()
42227     {
42228         if (!this.form.progressUrl) {
42229             return;
42230         }
42231         
42232         if (!this.haveProgress) {
42233             Roo.MessageBox.progress("Uploading", "Uploading");
42234         }
42235         if (this.uploadComplete) {
42236            Roo.MessageBox.hide();
42237            return;
42238         }
42239         
42240         this.haveProgress = true;
42241    
42242         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42243         
42244         var c = new Roo.data.Connection();
42245         c.request({
42246             url : this.form.progressUrl,
42247             params: {
42248                 id : uid
42249             },
42250             method: 'GET',
42251             success : function(req){
42252                //console.log(data);
42253                 var rdata = false;
42254                 var edata;
42255                 try  {
42256                    rdata = Roo.decode(req.responseText)
42257                 } catch (e) {
42258                     Roo.log("Invalid data from server..");
42259                     Roo.log(edata);
42260                     return;
42261                 }
42262                 if (!rdata || !rdata.success) {
42263                     Roo.log(rdata);
42264                     return;
42265                 }
42266                 var data = rdata.data;
42267                 
42268                 if (this.uploadComplete) {
42269                    Roo.MessageBox.hide();
42270                    return;
42271                 }
42272                    
42273                 if (data){
42274                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42275                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42276                     );
42277                 }
42278                 this.uploadProgress.defer(2000,this);
42279             },
42280        
42281             failure: function(data) {
42282                 Roo.log('progress url failed ');
42283                 Roo.log(data);
42284             },
42285             scope : this
42286         });
42287            
42288     },
42289     
42290     
42291     run : function()
42292     {
42293         // run get Values on the form, so it syncs any secondary forms.
42294         this.form.getValues();
42295         
42296         var o = this.options;
42297         var method = this.getMethod();
42298         var isPost = method == 'POST';
42299         if(o.clientValidation === false || this.form.isValid()){
42300             
42301             if (this.form.progressUrl) {
42302                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42303                     (new Date() * 1) + '' + Math.random());
42304                     
42305             } 
42306             
42307             
42308             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42309                 form:this.form.el.dom,
42310                 url:this.getUrl(!isPost),
42311                 method: method,
42312                 params:isPost ? this.getParams() : null,
42313                 isUpload: this.form.fileUpload
42314             }));
42315             
42316             this.uploadProgress();
42317
42318         }else if (o.clientValidation !== false){ // client validation failed
42319             this.failureType = Roo.form.Action.CLIENT_INVALID;
42320             this.form.afterAction(this, false);
42321         }
42322     },
42323
42324     success : function(response)
42325     {
42326         this.uploadComplete= true;
42327         if (this.haveProgress) {
42328             Roo.MessageBox.hide();
42329         }
42330         
42331         
42332         var result = this.processResponse(response);
42333         if(result === true || result.success){
42334             this.form.afterAction(this, true);
42335             return;
42336         }
42337         if(result.errors){
42338             this.form.markInvalid(result.errors);
42339             this.failureType = Roo.form.Action.SERVER_INVALID;
42340         }
42341         this.form.afterAction(this, false);
42342     },
42343     failure : function(response)
42344     {
42345         this.uploadComplete= true;
42346         if (this.haveProgress) {
42347             Roo.MessageBox.hide();
42348         }
42349         
42350         this.response = response;
42351         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42352         this.form.afterAction(this, false);
42353     },
42354     
42355     handleResponse : function(response){
42356         if(this.form.errorReader){
42357             var rs = this.form.errorReader.read(response);
42358             var errors = [];
42359             if(rs.records){
42360                 for(var i = 0, len = rs.records.length; i < len; i++) {
42361                     var r = rs.records[i];
42362                     errors[i] = r.data;
42363                 }
42364             }
42365             if(errors.length < 1){
42366                 errors = null;
42367             }
42368             return {
42369                 success : rs.success,
42370                 errors : errors
42371             };
42372         }
42373         var ret = false;
42374         try {
42375             ret = Roo.decode(response.responseText);
42376         } catch (e) {
42377             ret = {
42378                 success: false,
42379                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42380                 errors : []
42381             };
42382         }
42383         return ret;
42384         
42385     }
42386 });
42387
42388
42389 Roo.form.Action.Load = function(form, options){
42390     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42391     this.reader = this.form.reader;
42392 };
42393
42394 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42395     type : 'load',
42396
42397     run : function(){
42398         
42399         Roo.Ajax.request(Roo.apply(
42400                 this.createCallback(), {
42401                     method:this.getMethod(),
42402                     url:this.getUrl(false),
42403                     params:this.getParams()
42404         }));
42405     },
42406
42407     success : function(response){
42408         
42409         var result = this.processResponse(response);
42410         if(result === true || !result.success || !result.data){
42411             this.failureType = Roo.form.Action.LOAD_FAILURE;
42412             this.form.afterAction(this, false);
42413             return;
42414         }
42415         this.form.clearInvalid();
42416         this.form.setValues(result.data);
42417         this.form.afterAction(this, true);
42418     },
42419
42420     handleResponse : function(response){
42421         if(this.form.reader){
42422             var rs = this.form.reader.read(response);
42423             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42424             return {
42425                 success : rs.success,
42426                 data : data
42427             };
42428         }
42429         return Roo.decode(response.responseText);
42430     }
42431 });
42432
42433 Roo.form.Action.ACTION_TYPES = {
42434     'load' : Roo.form.Action.Load,
42435     'submit' : Roo.form.Action.Submit
42436 };/*
42437  * Based on:
42438  * Ext JS Library 1.1.1
42439  * Copyright(c) 2006-2007, Ext JS, LLC.
42440  *
42441  * Originally Released Under LGPL - original licence link has changed is not relivant.
42442  *
42443  * Fork - LGPL
42444  * <script type="text/javascript">
42445  */
42446  
42447 /**
42448  * @class Roo.form.Layout
42449  * @extends Roo.Component
42450  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42451  * @constructor
42452  * @param {Object} config Configuration options
42453  */
42454 Roo.form.Layout = function(config){
42455     var xitems = [];
42456     if (config.items) {
42457         xitems = config.items;
42458         delete config.items;
42459     }
42460     Roo.form.Layout.superclass.constructor.call(this, config);
42461     this.stack = [];
42462     Roo.each(xitems, this.addxtype, this);
42463      
42464 };
42465
42466 Roo.extend(Roo.form.Layout, Roo.Component, {
42467     /**
42468      * @cfg {String/Object} autoCreate
42469      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42470      */
42471     /**
42472      * @cfg {String/Object/Function} style
42473      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42474      * a function which returns such a specification.
42475      */
42476     /**
42477      * @cfg {String} labelAlign
42478      * Valid values are "left," "top" and "right" (defaults to "left")
42479      */
42480     /**
42481      * @cfg {Number} labelWidth
42482      * Fixed width in pixels of all field labels (defaults to undefined)
42483      */
42484     /**
42485      * @cfg {Boolean} clear
42486      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42487      */
42488     clear : true,
42489     /**
42490      * @cfg {String} labelSeparator
42491      * The separator to use after field labels (defaults to ':')
42492      */
42493     labelSeparator : ':',
42494     /**
42495      * @cfg {Boolean} hideLabels
42496      * True to suppress the display of field labels in this layout (defaults to false)
42497      */
42498     hideLabels : false,
42499
42500     // private
42501     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42502     
42503     isLayout : true,
42504     
42505     // private
42506     onRender : function(ct, position){
42507         if(this.el){ // from markup
42508             this.el = Roo.get(this.el);
42509         }else {  // generate
42510             var cfg = this.getAutoCreate();
42511             this.el = ct.createChild(cfg, position);
42512         }
42513         if(this.style){
42514             this.el.applyStyles(this.style);
42515         }
42516         if(this.labelAlign){
42517             this.el.addClass('x-form-label-'+this.labelAlign);
42518         }
42519         if(this.hideLabels){
42520             this.labelStyle = "display:none";
42521             this.elementStyle = "padding-left:0;";
42522         }else{
42523             if(typeof this.labelWidth == 'number'){
42524                 this.labelStyle = "width:"+this.labelWidth+"px;";
42525                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42526             }
42527             if(this.labelAlign == 'top'){
42528                 this.labelStyle = "width:auto;";
42529                 this.elementStyle = "padding-left:0;";
42530             }
42531         }
42532         var stack = this.stack;
42533         var slen = stack.length;
42534         if(slen > 0){
42535             if(!this.fieldTpl){
42536                 var t = new Roo.Template(
42537                     '<div class="x-form-item {5}">',
42538                         '<label for="{0}" style="{2}">{1}{4}</label>',
42539                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42540                         '</div>',
42541                     '</div><div class="x-form-clear-left"></div>'
42542                 );
42543                 t.disableFormats = true;
42544                 t.compile();
42545                 Roo.form.Layout.prototype.fieldTpl = t;
42546             }
42547             for(var i = 0; i < slen; i++) {
42548                 if(stack[i].isFormField){
42549                     this.renderField(stack[i]);
42550                 }else{
42551                     this.renderComponent(stack[i]);
42552                 }
42553             }
42554         }
42555         if(this.clear){
42556             this.el.createChild({cls:'x-form-clear'});
42557         }
42558     },
42559
42560     // private
42561     renderField : function(f){
42562         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42563                f.id, //0
42564                f.fieldLabel, //1
42565                f.labelStyle||this.labelStyle||'', //2
42566                this.elementStyle||'', //3
42567                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42568                f.itemCls||this.itemCls||''  //5
42569        ], true).getPrevSibling());
42570     },
42571
42572     // private
42573     renderComponent : function(c){
42574         c.render(c.isLayout ? this.el : this.el.createChild());    
42575     },
42576     /**
42577      * Adds a object form elements (using the xtype property as the factory method.)
42578      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42579      * @param {Object} config 
42580      */
42581     addxtype : function(o)
42582     {
42583         // create the lement.
42584         o.form = this.form;
42585         var fe = Roo.factory(o, Roo.form);
42586         this.form.allItems.push(fe);
42587         this.stack.push(fe);
42588         
42589         if (fe.isFormField) {
42590             this.form.items.add(fe);
42591         }
42592          
42593         return fe;
42594     }
42595 });
42596
42597 /**
42598  * @class Roo.form.Column
42599  * @extends Roo.form.Layout
42600  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42601  * @constructor
42602  * @param {Object} config Configuration options
42603  */
42604 Roo.form.Column = function(config){
42605     Roo.form.Column.superclass.constructor.call(this, config);
42606 };
42607
42608 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42609     /**
42610      * @cfg {Number/String} width
42611      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42612      */
42613     /**
42614      * @cfg {String/Object} autoCreate
42615      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42616      */
42617
42618     // private
42619     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42620
42621     // private
42622     onRender : function(ct, position){
42623         Roo.form.Column.superclass.onRender.call(this, ct, position);
42624         if(this.width){
42625             this.el.setWidth(this.width);
42626         }
42627     }
42628 });
42629
42630
42631 /**
42632  * @class Roo.form.Row
42633  * @extends Roo.form.Layout
42634  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42635  * @constructor
42636  * @param {Object} config Configuration options
42637  */
42638
42639  
42640 Roo.form.Row = function(config){
42641     Roo.form.Row.superclass.constructor.call(this, config);
42642 };
42643  
42644 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42645       /**
42646      * @cfg {Number/String} width
42647      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42648      */
42649     /**
42650      * @cfg {Number/String} height
42651      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42652      */
42653     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42654     
42655     padWidth : 20,
42656     // private
42657     onRender : function(ct, position){
42658         //console.log('row render');
42659         if(!this.rowTpl){
42660             var t = new Roo.Template(
42661                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42662                     '<label for="{0}" style="{2}">{1}{4}</label>',
42663                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42664                     '</div>',
42665                 '</div>'
42666             );
42667             t.disableFormats = true;
42668             t.compile();
42669             Roo.form.Layout.prototype.rowTpl = t;
42670         }
42671         this.fieldTpl = this.rowTpl;
42672         
42673         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42674         var labelWidth = 100;
42675         
42676         if ((this.labelAlign != 'top')) {
42677             if (typeof this.labelWidth == 'number') {
42678                 labelWidth = this.labelWidth
42679             }
42680             this.padWidth =  20 + labelWidth;
42681             
42682         }
42683         
42684         Roo.form.Column.superclass.onRender.call(this, ct, position);
42685         if(this.width){
42686             this.el.setWidth(this.width);
42687         }
42688         if(this.height){
42689             this.el.setHeight(this.height);
42690         }
42691     },
42692     
42693     // private
42694     renderField : function(f){
42695         f.fieldEl = this.fieldTpl.append(this.el, [
42696                f.id, f.fieldLabel,
42697                f.labelStyle||this.labelStyle||'',
42698                this.elementStyle||'',
42699                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42700                f.itemCls||this.itemCls||'',
42701                f.width ? f.width + this.padWidth : 160 + this.padWidth
42702        ],true);
42703     }
42704 });
42705  
42706
42707 /**
42708  * @class Roo.form.FieldSet
42709  * @extends Roo.form.Layout
42710  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42711  * @constructor
42712  * @param {Object} config Configuration options
42713  */
42714 Roo.form.FieldSet = function(config){
42715     Roo.form.FieldSet.superclass.constructor.call(this, config);
42716 };
42717
42718 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42719     /**
42720      * @cfg {String} legend
42721      * The text to display as the legend for the FieldSet (defaults to '')
42722      */
42723     /**
42724      * @cfg {String/Object} autoCreate
42725      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42726      */
42727
42728     // private
42729     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42730
42731     // private
42732     onRender : function(ct, position){
42733         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42734         if(this.legend){
42735             this.setLegend(this.legend);
42736         }
42737     },
42738
42739     // private
42740     setLegend : function(text){
42741         if(this.rendered){
42742             this.el.child('legend').update(text);
42743         }
42744     }
42745 });/*
42746  * Based on:
42747  * Ext JS Library 1.1.1
42748  * Copyright(c) 2006-2007, Ext JS, LLC.
42749  *
42750  * Originally Released Under LGPL - original licence link has changed is not relivant.
42751  *
42752  * Fork - LGPL
42753  * <script type="text/javascript">
42754  */
42755 /**
42756  * @class Roo.form.VTypes
42757  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42758  * @singleton
42759  */
42760 Roo.form.VTypes = function(){
42761     // closure these in so they are only created once.
42762     var alpha = /^[a-zA-Z_]+$/;
42763     var alphanum = /^[a-zA-Z0-9_]+$/;
42764     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42765     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42766
42767     // All these messages and functions are configurable
42768     return {
42769         /**
42770          * The function used to validate email addresses
42771          * @param {String} value The email address
42772          */
42773         'email' : function(v){
42774             return email.test(v);
42775         },
42776         /**
42777          * The error text to display when the email validation function returns false
42778          * @type String
42779          */
42780         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42781         /**
42782          * The keystroke filter mask to be applied on email input
42783          * @type RegExp
42784          */
42785         'emailMask' : /[a-z0-9_\.\-@]/i,
42786
42787         /**
42788          * The function used to validate URLs
42789          * @param {String} value The URL
42790          */
42791         'url' : function(v){
42792             return url.test(v);
42793         },
42794         /**
42795          * The error text to display when the url validation function returns false
42796          * @type String
42797          */
42798         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42799         
42800         /**
42801          * The function used to validate alpha values
42802          * @param {String} value The value
42803          */
42804         'alpha' : function(v){
42805             return alpha.test(v);
42806         },
42807         /**
42808          * The error text to display when the alpha validation function returns false
42809          * @type String
42810          */
42811         'alphaText' : 'This field should only contain letters and _',
42812         /**
42813          * The keystroke filter mask to be applied on alpha input
42814          * @type RegExp
42815          */
42816         'alphaMask' : /[a-z_]/i,
42817
42818         /**
42819          * The function used to validate alphanumeric values
42820          * @param {String} value The value
42821          */
42822         'alphanum' : function(v){
42823             return alphanum.test(v);
42824         },
42825         /**
42826          * The error text to display when the alphanumeric validation function returns false
42827          * @type String
42828          */
42829         'alphanumText' : 'This field should only contain letters, numbers and _',
42830         /**
42831          * The keystroke filter mask to be applied on alphanumeric input
42832          * @type RegExp
42833          */
42834         'alphanumMask' : /[a-z0-9_]/i
42835     };
42836 }();//<script type="text/javascript">
42837
42838 /**
42839  * @class Roo.form.FCKeditor
42840  * @extends Roo.form.TextArea
42841  * Wrapper around the FCKEditor http://www.fckeditor.net
42842  * @constructor
42843  * Creates a new FCKeditor
42844  * @param {Object} config Configuration options
42845  */
42846 Roo.form.FCKeditor = function(config){
42847     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42848     this.addEvents({
42849          /**
42850          * @event editorinit
42851          * Fired when the editor is initialized - you can add extra handlers here..
42852          * @param {FCKeditor} this
42853          * @param {Object} the FCK object.
42854          */
42855         editorinit : true
42856     });
42857     
42858     
42859 };
42860 Roo.form.FCKeditor.editors = { };
42861 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42862 {
42863     //defaultAutoCreate : {
42864     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42865     //},
42866     // private
42867     /**
42868      * @cfg {Object} fck options - see fck manual for details.
42869      */
42870     fckconfig : false,
42871     
42872     /**
42873      * @cfg {Object} fck toolbar set (Basic or Default)
42874      */
42875     toolbarSet : 'Basic',
42876     /**
42877      * @cfg {Object} fck BasePath
42878      */ 
42879     basePath : '/fckeditor/',
42880     
42881     
42882     frame : false,
42883     
42884     value : '',
42885     
42886    
42887     onRender : function(ct, position)
42888     {
42889         if(!this.el){
42890             this.defaultAutoCreate = {
42891                 tag: "textarea",
42892                 style:"width:300px;height:60px;",
42893                 autocomplete: "off"
42894             };
42895         }
42896         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42897         /*
42898         if(this.grow){
42899             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42900             if(this.preventScrollbars){
42901                 this.el.setStyle("overflow", "hidden");
42902             }
42903             this.el.setHeight(this.growMin);
42904         }
42905         */
42906         //console.log('onrender' + this.getId() );
42907         Roo.form.FCKeditor.editors[this.getId()] = this;
42908          
42909
42910         this.replaceTextarea() ;
42911         
42912     },
42913     
42914     getEditor : function() {
42915         return this.fckEditor;
42916     },
42917     /**
42918      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42919      * @param {Mixed} value The value to set
42920      */
42921     
42922     
42923     setValue : function(value)
42924     {
42925         //console.log('setValue: ' + value);
42926         
42927         if(typeof(value) == 'undefined') { // not sure why this is happending...
42928             return;
42929         }
42930         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42931         
42932         //if(!this.el || !this.getEditor()) {
42933         //    this.value = value;
42934             //this.setValue.defer(100,this,[value]);    
42935         //    return;
42936         //} 
42937         
42938         if(!this.getEditor()) {
42939             return;
42940         }
42941         
42942         this.getEditor().SetData(value);
42943         
42944         //
42945
42946     },
42947
42948     /**
42949      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42950      * @return {Mixed} value The field value
42951      */
42952     getValue : function()
42953     {
42954         
42955         if (this.frame && this.frame.dom.style.display == 'none') {
42956             return Roo.form.FCKeditor.superclass.getValue.call(this);
42957         }
42958         
42959         if(!this.el || !this.getEditor()) {
42960            
42961            // this.getValue.defer(100,this); 
42962             return this.value;
42963         }
42964        
42965         
42966         var value=this.getEditor().GetData();
42967         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42968         return Roo.form.FCKeditor.superclass.getValue.call(this);
42969         
42970
42971     },
42972
42973     /**
42974      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42975      * @return {Mixed} value The field value
42976      */
42977     getRawValue : function()
42978     {
42979         if (this.frame && this.frame.dom.style.display == 'none') {
42980             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42981         }
42982         
42983         if(!this.el || !this.getEditor()) {
42984             //this.getRawValue.defer(100,this); 
42985             return this.value;
42986             return;
42987         }
42988         
42989         
42990         
42991         var value=this.getEditor().GetData();
42992         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42993         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42994          
42995     },
42996     
42997     setSize : function(w,h) {
42998         
42999         
43000         
43001         //if (this.frame && this.frame.dom.style.display == 'none') {
43002         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43003         //    return;
43004         //}
43005         //if(!this.el || !this.getEditor()) {
43006         //    this.setSize.defer(100,this, [w,h]); 
43007         //    return;
43008         //}
43009         
43010         
43011         
43012         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43013         
43014         this.frame.dom.setAttribute('width', w);
43015         this.frame.dom.setAttribute('height', h);
43016         this.frame.setSize(w,h);
43017         
43018     },
43019     
43020     toggleSourceEdit : function(value) {
43021         
43022       
43023          
43024         this.el.dom.style.display = value ? '' : 'none';
43025         this.frame.dom.style.display = value ?  'none' : '';
43026         
43027     },
43028     
43029     
43030     focus: function(tag)
43031     {
43032         if (this.frame.dom.style.display == 'none') {
43033             return Roo.form.FCKeditor.superclass.focus.call(this);
43034         }
43035         if(!this.el || !this.getEditor()) {
43036             this.focus.defer(100,this, [tag]); 
43037             return;
43038         }
43039         
43040         
43041         
43042         
43043         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43044         this.getEditor().Focus();
43045         if (tgs.length) {
43046             if (!this.getEditor().Selection.GetSelection()) {
43047                 this.focus.defer(100,this, [tag]); 
43048                 return;
43049             }
43050             
43051             
43052             var r = this.getEditor().EditorDocument.createRange();
43053             r.setStart(tgs[0],0);
43054             r.setEnd(tgs[0],0);
43055             this.getEditor().Selection.GetSelection().removeAllRanges();
43056             this.getEditor().Selection.GetSelection().addRange(r);
43057             this.getEditor().Focus();
43058         }
43059         
43060     },
43061     
43062     
43063     
43064     replaceTextarea : function()
43065     {
43066         if ( document.getElementById( this.getId() + '___Frame' ) )
43067             return ;
43068         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43069         //{
43070             // We must check the elements firstly using the Id and then the name.
43071         var oTextarea = document.getElementById( this.getId() );
43072         
43073         var colElementsByName = document.getElementsByName( this.getId() ) ;
43074          
43075         oTextarea.style.display = 'none' ;
43076
43077         if ( oTextarea.tabIndex ) {            
43078             this.TabIndex = oTextarea.tabIndex ;
43079         }
43080         
43081         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43082         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43083         this.frame = Roo.get(this.getId() + '___Frame')
43084     },
43085     
43086     _getConfigHtml : function()
43087     {
43088         var sConfig = '' ;
43089
43090         for ( var o in this.fckconfig ) {
43091             sConfig += sConfig.length > 0  ? '&amp;' : '';
43092             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43093         }
43094
43095         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43096     },
43097     
43098     
43099     _getIFrameHtml : function()
43100     {
43101         var sFile = 'fckeditor.html' ;
43102         /* no idea what this is about..
43103         try
43104         {
43105             if ( (/fcksource=true/i).test( window.top.location.search ) )
43106                 sFile = 'fckeditor.original.html' ;
43107         }
43108         catch (e) { 
43109         */
43110
43111         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43112         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43113         
43114         
43115         var html = '<iframe id="' + this.getId() +
43116             '___Frame" src="' + sLink +
43117             '" width="' + this.width +
43118             '" height="' + this.height + '"' +
43119             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43120             ' frameborder="0" scrolling="no"></iframe>' ;
43121
43122         return html ;
43123     },
43124     
43125     _insertHtmlBefore : function( html, element )
43126     {
43127         if ( element.insertAdjacentHTML )       {
43128             // IE
43129             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43130         } else { // Gecko
43131             var oRange = document.createRange() ;
43132             oRange.setStartBefore( element ) ;
43133             var oFragment = oRange.createContextualFragment( html );
43134             element.parentNode.insertBefore( oFragment, element ) ;
43135         }
43136     }
43137     
43138     
43139   
43140     
43141     
43142     
43143     
43144
43145 });
43146
43147 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43148
43149 function FCKeditor_OnComplete(editorInstance){
43150     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43151     f.fckEditor = editorInstance;
43152     //console.log("loaded");
43153     f.fireEvent('editorinit', f, editorInstance);
43154
43155   
43156
43157  
43158
43159
43160
43161
43162
43163
43164
43165
43166
43167
43168
43169
43170
43171
43172
43173 //<script type="text/javascript">
43174 /**
43175  * @class Roo.form.GridField
43176  * @extends Roo.form.Field
43177  * Embed a grid (or editable grid into a form)
43178  * STATUS ALPHA
43179  * 
43180  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43181  * it needs 
43182  * xgrid.store = Roo.data.Store
43183  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43184  * xgrid.store.reader = Roo.data.JsonReader 
43185  * 
43186  * 
43187  * @constructor
43188  * Creates a new GridField
43189  * @param {Object} config Configuration options
43190  */
43191 Roo.form.GridField = function(config){
43192     Roo.form.GridField.superclass.constructor.call(this, config);
43193      
43194 };
43195
43196 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43197     /**
43198      * @cfg {Number} width  - used to restrict width of grid..
43199      */
43200     width : 100,
43201     /**
43202      * @cfg {Number} height - used to restrict height of grid..
43203      */
43204     height : 50,
43205      /**
43206      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43207          * 
43208          *}
43209      */
43210     xgrid : false, 
43211     /**
43212      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43213      * {tag: "input", type: "checkbox", autocomplete: "off"})
43214      */
43215    // defaultAutoCreate : { tag: 'div' },
43216     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43217     /**
43218      * @cfg {String} addTitle Text to include for adding a title.
43219      */
43220     addTitle : false,
43221     //
43222     onResize : function(){
43223         Roo.form.Field.superclass.onResize.apply(this, arguments);
43224     },
43225
43226     initEvents : function(){
43227         // Roo.form.Checkbox.superclass.initEvents.call(this);
43228         // has no events...
43229        
43230     },
43231
43232
43233     getResizeEl : function(){
43234         return this.wrap;
43235     },
43236
43237     getPositionEl : function(){
43238         return this.wrap;
43239     },
43240
43241     // private
43242     onRender : function(ct, position){
43243         
43244         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43245         var style = this.style;
43246         delete this.style;
43247         
43248         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43249         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43250         this.viewEl = this.wrap.createChild({ tag: 'div' });
43251         if (style) {
43252             this.viewEl.applyStyles(style);
43253         }
43254         if (this.width) {
43255             this.viewEl.setWidth(this.width);
43256         }
43257         if (this.height) {
43258             this.viewEl.setHeight(this.height);
43259         }
43260         //if(this.inputValue !== undefined){
43261         //this.setValue(this.value);
43262         
43263         
43264         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43265         
43266         
43267         this.grid.render();
43268         this.grid.getDataSource().on('remove', this.refreshValue, this);
43269         this.grid.getDataSource().on('update', this.refreshValue, this);
43270         this.grid.on('afteredit', this.refreshValue, this);
43271  
43272     },
43273      
43274     
43275     /**
43276      * Sets the value of the item. 
43277      * @param {String} either an object  or a string..
43278      */
43279     setValue : function(v){
43280         //this.value = v;
43281         v = v || []; // empty set..
43282         // this does not seem smart - it really only affects memoryproxy grids..
43283         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43284             var ds = this.grid.getDataSource();
43285             // assumes a json reader..
43286             var data = {}
43287             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43288             ds.loadData( data);
43289         }
43290         // clear selection so it does not get stale.
43291         if (this.grid.sm) { 
43292             this.grid.sm.clearSelections();
43293         }
43294         
43295         Roo.form.GridField.superclass.setValue.call(this, v);
43296         this.refreshValue();
43297         // should load data in the grid really....
43298     },
43299     
43300     // private
43301     refreshValue: function() {
43302          var val = [];
43303         this.grid.getDataSource().each(function(r) {
43304             val.push(r.data);
43305         });
43306         this.el.dom.value = Roo.encode(val);
43307     }
43308     
43309      
43310     
43311     
43312 });/*
43313  * Based on:
43314  * Ext JS Library 1.1.1
43315  * Copyright(c) 2006-2007, Ext JS, LLC.
43316  *
43317  * Originally Released Under LGPL - original licence link has changed is not relivant.
43318  *
43319  * Fork - LGPL
43320  * <script type="text/javascript">
43321  */
43322 /**
43323  * @class Roo.form.DisplayField
43324  * @extends Roo.form.Field
43325  * A generic Field to display non-editable data.
43326  * @constructor
43327  * Creates a new Display Field item.
43328  * @param {Object} config Configuration options
43329  */
43330 Roo.form.DisplayField = function(config){
43331     Roo.form.DisplayField.superclass.constructor.call(this, config);
43332     
43333 };
43334
43335 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43336     inputType:      'hidden',
43337     allowBlank:     true,
43338     readOnly:         true,
43339     
43340  
43341     /**
43342      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43343      */
43344     focusClass : undefined,
43345     /**
43346      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43347      */
43348     fieldClass: 'x-form-field',
43349     
43350      /**
43351      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43352      */
43353     valueRenderer: undefined,
43354     
43355     width: 100,
43356     /**
43357      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43358      * {tag: "input", type: "checkbox", autocomplete: "off"})
43359      */
43360      
43361  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43362
43363     onResize : function(){
43364         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43365         
43366     },
43367
43368     initEvents : function(){
43369         // Roo.form.Checkbox.superclass.initEvents.call(this);
43370         // has no events...
43371        
43372     },
43373
43374
43375     getResizeEl : function(){
43376         return this.wrap;
43377     },
43378
43379     getPositionEl : function(){
43380         return this.wrap;
43381     },
43382
43383     // private
43384     onRender : function(ct, position){
43385         
43386         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43387         //if(this.inputValue !== undefined){
43388         this.wrap = this.el.wrap();
43389         
43390         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43391         
43392         if (this.bodyStyle) {
43393             this.viewEl.applyStyles(this.bodyStyle);
43394         }
43395         //this.viewEl.setStyle('padding', '2px');
43396         
43397         this.setValue(this.value);
43398         
43399     },
43400 /*
43401     // private
43402     initValue : Roo.emptyFn,
43403
43404   */
43405
43406         // private
43407     onClick : function(){
43408         
43409     },
43410
43411     /**
43412      * Sets the checked state of the checkbox.
43413      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43414      */
43415     setValue : function(v){
43416         this.value = v;
43417         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43418         // this might be called before we have a dom element..
43419         if (!this.viewEl) {
43420             return;
43421         }
43422         this.viewEl.dom.innerHTML = html;
43423         Roo.form.DisplayField.superclass.setValue.call(this, v);
43424
43425     }
43426 });/*
43427  * 
43428  * Licence- LGPL
43429  * 
43430  */
43431
43432 /**
43433  * @class Roo.form.DayPicker
43434  * @extends Roo.form.Field
43435  * A Day picker show [M] [T] [W] ....
43436  * @constructor
43437  * Creates a new Day Picker
43438  * @param {Object} config Configuration options
43439  */
43440 Roo.form.DayPicker= function(config){
43441     Roo.form.DayPicker.superclass.constructor.call(this, config);
43442      
43443 };
43444
43445 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43446     /**
43447      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43448      */
43449     focusClass : undefined,
43450     /**
43451      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43452      */
43453     fieldClass: "x-form-field",
43454    
43455     /**
43456      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43457      * {tag: "input", type: "checkbox", autocomplete: "off"})
43458      */
43459     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43460     
43461    
43462     actionMode : 'viewEl', 
43463     //
43464     // private
43465  
43466     inputType : 'hidden',
43467     
43468      
43469     inputElement: false, // real input element?
43470     basedOn: false, // ????
43471     
43472     isFormField: true, // not sure where this is needed!!!!
43473
43474     onResize : function(){
43475         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43476         if(!this.boxLabel){
43477             this.el.alignTo(this.wrap, 'c-c');
43478         }
43479     },
43480
43481     initEvents : function(){
43482         Roo.form.Checkbox.superclass.initEvents.call(this);
43483         this.el.on("click", this.onClick,  this);
43484         this.el.on("change", this.onClick,  this);
43485     },
43486
43487
43488     getResizeEl : function(){
43489         return this.wrap;
43490     },
43491
43492     getPositionEl : function(){
43493         return this.wrap;
43494     },
43495
43496     
43497     // private
43498     onRender : function(ct, position){
43499         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43500        
43501         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43502         
43503         var r1 = '<table><tr>';
43504         var r2 = '<tr class="x-form-daypick-icons">';
43505         for (var i=0; i < 7; i++) {
43506             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43507             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43508         }
43509         
43510         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43511         viewEl.select('img').on('click', this.onClick, this);
43512         this.viewEl = viewEl;   
43513         
43514         
43515         // this will not work on Chrome!!!
43516         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43517         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43518         
43519         
43520           
43521
43522     },
43523
43524     // private
43525     initValue : Roo.emptyFn,
43526
43527     /**
43528      * Returns the checked state of the checkbox.
43529      * @return {Boolean} True if checked, else false
43530      */
43531     getValue : function(){
43532         return this.el.dom.value;
43533         
43534     },
43535
43536         // private
43537     onClick : function(e){ 
43538         //this.setChecked(!this.checked);
43539         Roo.get(e.target).toggleClass('x-menu-item-checked');
43540         this.refreshValue();
43541         //if(this.el.dom.checked != this.checked){
43542         //    this.setValue(this.el.dom.checked);
43543        // }
43544     },
43545     
43546     // private
43547     refreshValue : function()
43548     {
43549         var val = '';
43550         this.viewEl.select('img',true).each(function(e,i,n)  {
43551             val += e.is(".x-menu-item-checked") ? String(n) : '';
43552         });
43553         this.setValue(val, true);
43554     },
43555
43556     /**
43557      * Sets the checked state of the checkbox.
43558      * On is always based on a string comparison between inputValue and the param.
43559      * @param {Boolean/String} value - the value to set 
43560      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43561      */
43562     setValue : function(v,suppressEvent){
43563         if (!this.el.dom) {
43564             return;
43565         }
43566         var old = this.el.dom.value ;
43567         this.el.dom.value = v;
43568         if (suppressEvent) {
43569             return ;
43570         }
43571          
43572         // update display..
43573         this.viewEl.select('img',true).each(function(e,i,n)  {
43574             
43575             var on = e.is(".x-menu-item-checked");
43576             var newv = v.indexOf(String(n)) > -1;
43577             if (on != newv) {
43578                 e.toggleClass('x-menu-item-checked');
43579             }
43580             
43581         });
43582         
43583         
43584         this.fireEvent('change', this, v, old);
43585         
43586         
43587     },
43588    
43589     // handle setting of hidden value by some other method!!?!?
43590     setFromHidden: function()
43591     {
43592         if(!this.el){
43593             return;
43594         }
43595         //console.log("SET FROM HIDDEN");
43596         //alert('setFrom hidden');
43597         this.setValue(this.el.dom.value);
43598     },
43599     
43600     onDestroy : function()
43601     {
43602         if(this.viewEl){
43603             Roo.get(this.viewEl).remove();
43604         }
43605          
43606         Roo.form.DayPicker.superclass.onDestroy.call(this);
43607     }
43608
43609 });/*
43610  * RooJS Library 1.1.1
43611  * Copyright(c) 2008-2011  Alan Knowles
43612  *
43613  * License - LGPL
43614  */
43615  
43616
43617 /**
43618  * @class Roo.form.ComboCheck
43619  * @extends Roo.form.ComboBox
43620  * A combobox for multiple select items.
43621  *
43622  * FIXME - could do with a reset button..
43623  * 
43624  * @constructor
43625  * Create a new ComboCheck
43626  * @param {Object} config Configuration options
43627  */
43628 Roo.form.ComboCheck = function(config){
43629     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43630     // should verify some data...
43631     // like
43632     // hiddenName = required..
43633     // displayField = required
43634     // valudField == required
43635     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43636     var _t = this;
43637     Roo.each(req, function(e) {
43638         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43639             throw "Roo.form.ComboCheck : missing value for: " + e;
43640         }
43641     });
43642     
43643     
43644 };
43645
43646 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43647      
43648      
43649     editable : false,
43650      
43651     selectedClass: 'x-menu-item-checked', 
43652     
43653     // private
43654     onRender : function(ct, position){
43655         var _t = this;
43656         
43657         
43658         
43659         if(!this.tpl){
43660             var cls = 'x-combo-list';
43661
43662             
43663             this.tpl =  new Roo.Template({
43664                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43665                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43666                    '<span>{' + this.displayField + '}</span>' +
43667                     '</div>' 
43668                 
43669             });
43670         }
43671  
43672         
43673         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43674         this.view.singleSelect = false;
43675         this.view.multiSelect = true;
43676         this.view.toggleSelect = true;
43677         this.pageTb.add(new Roo.Toolbar.Fill(), {
43678             
43679             text: 'Done',
43680             handler: function()
43681             {
43682                 _t.collapse();
43683             }
43684         });
43685     },
43686     
43687     onViewOver : function(e, t){
43688         // do nothing...
43689         return;
43690         
43691     },
43692     
43693     onViewClick : function(doFocus,index){
43694         return;
43695         
43696     },
43697     select: function () {
43698         //Roo.log("SELECT CALLED");
43699     },
43700      
43701     selectByValue : function(xv, scrollIntoView){
43702         var ar = this.getValueArray();
43703         var sels = [];
43704         
43705         Roo.each(ar, function(v) {
43706             if(v === undefined || v === null){
43707                 return;
43708             }
43709             var r = this.findRecord(this.valueField, v);
43710             if(r){
43711                 sels.push(this.store.indexOf(r))
43712                 
43713             }
43714         },this);
43715         this.view.select(sels);
43716         return false;
43717     },
43718     
43719     
43720     
43721     onSelect : function(record, index){
43722        // Roo.log("onselect Called");
43723        // this is only called by the clear button now..
43724         this.view.clearSelections();
43725         this.setValue('[]');
43726         if (this.value != this.valueBefore) {
43727             this.fireEvent('change', this, this.value, this.valueBefore);
43728         }
43729     },
43730     getValueArray : function()
43731     {
43732         var ar = [] ;
43733         
43734         try {
43735             //Roo.log(this.value);
43736             if (typeof(this.value) == 'undefined') {
43737                 return [];
43738             }
43739             var ar = Roo.decode(this.value);
43740             return  ar instanceof Array ? ar : []; //?? valid?
43741             
43742         } catch(e) {
43743             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43744             return [];
43745         }
43746          
43747     },
43748     expand : function ()
43749     {
43750         Roo.form.ComboCheck.superclass.expand.call(this);
43751         this.valueBefore = this.value;
43752         
43753
43754     },
43755     
43756     collapse : function(){
43757         Roo.form.ComboCheck.superclass.collapse.call(this);
43758         var sl = this.view.getSelectedIndexes();
43759         var st = this.store;
43760         var nv = [];
43761         var tv = [];
43762         var r;
43763         Roo.each(sl, function(i) {
43764             r = st.getAt(i);
43765             nv.push(r.get(this.valueField));
43766         },this);
43767         this.setValue(Roo.encode(nv));
43768         if (this.value != this.valueBefore) {
43769
43770             this.fireEvent('change', this, this.value, this.valueBefore);
43771         }
43772         
43773     },
43774     
43775     setValue : function(v){
43776         // Roo.log(v);
43777         this.value = v;
43778         
43779         var vals = this.getValueArray();
43780         var tv = [];
43781         Roo.each(vals, function(k) {
43782             var r = this.findRecord(this.valueField, k);
43783             if(r){
43784                 tv.push(r.data[this.displayField]);
43785             }else if(this.valueNotFoundText !== undefined){
43786                 tv.push( this.valueNotFoundText );
43787             }
43788         },this);
43789        // Roo.log(tv);
43790         
43791         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43792         this.hiddenField.value = v;
43793         this.value = v;
43794     }
43795     
43796 });//<script type="text/javasscript">
43797  
43798
43799 /**
43800  * @class Roo.DDView
43801  * A DnD enabled version of Roo.View.
43802  * @param {Element/String} container The Element in which to create the View.
43803  * @param {String} tpl The template string used to create the markup for each element of the View
43804  * @param {Object} config The configuration properties. These include all the config options of
43805  * {@link Roo.View} plus some specific to this class.<br>
43806  * <p>
43807  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43808  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43809  * <p>
43810  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43811 .x-view-drag-insert-above {
43812         border-top:1px dotted #3366cc;
43813 }
43814 .x-view-drag-insert-below {
43815         border-bottom:1px dotted #3366cc;
43816 }
43817 </code></pre>
43818  * 
43819  */
43820  
43821 Roo.DDView = function(container, tpl, config) {
43822     Roo.DDView.superclass.constructor.apply(this, arguments);
43823     this.getEl().setStyle("outline", "0px none");
43824     this.getEl().unselectable();
43825     if (this.dragGroup) {
43826                 this.setDraggable(this.dragGroup.split(","));
43827     }
43828     if (this.dropGroup) {
43829                 this.setDroppable(this.dropGroup.split(","));
43830     }
43831     if (this.deletable) {
43832         this.setDeletable();
43833     }
43834     this.isDirtyFlag = false;
43835         this.addEvents({
43836                 "drop" : true
43837         });
43838 };
43839
43840 Roo.extend(Roo.DDView, Roo.View, {
43841 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43842 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43843 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43844 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43845
43846         isFormField: true,
43847
43848         reset: Roo.emptyFn,
43849         
43850         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43851
43852         validate: function() {
43853                 return true;
43854         },
43855         
43856         destroy: function() {
43857                 this.purgeListeners();
43858                 this.getEl.removeAllListeners();
43859                 this.getEl().remove();
43860                 if (this.dragZone) {
43861                         if (this.dragZone.destroy) {
43862                                 this.dragZone.destroy();
43863                         }
43864                 }
43865                 if (this.dropZone) {
43866                         if (this.dropZone.destroy) {
43867                                 this.dropZone.destroy();
43868                         }
43869                 }
43870         },
43871
43872 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43873         getName: function() {
43874                 return this.name;
43875         },
43876
43877 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43878         setValue: function(v) {
43879                 if (!this.store) {
43880                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43881                 }
43882                 var data = {};
43883                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43884                 this.store.proxy = new Roo.data.MemoryProxy(data);
43885                 this.store.load();
43886         },
43887
43888 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43889         getValue: function() {
43890                 var result = '(';
43891                 this.store.each(function(rec) {
43892                         result += rec.id + ',';
43893                 });
43894                 return result.substr(0, result.length - 1) + ')';
43895         },
43896         
43897         getIds: function() {
43898                 var i = 0, result = new Array(this.store.getCount());
43899                 this.store.each(function(rec) {
43900                         result[i++] = rec.id;
43901                 });
43902                 return result;
43903         },
43904         
43905         isDirty: function() {
43906                 return this.isDirtyFlag;
43907         },
43908
43909 /**
43910  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43911  *      whole Element becomes the target, and this causes the drop gesture to append.
43912  */
43913     getTargetFromEvent : function(e) {
43914                 var target = e.getTarget();
43915                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43916                 target = target.parentNode;
43917                 }
43918                 if (!target) {
43919                         target = this.el.dom.lastChild || this.el.dom;
43920                 }
43921                 return target;
43922     },
43923
43924 /**
43925  *      Create the drag data which consists of an object which has the property "ddel" as
43926  *      the drag proxy element. 
43927  */
43928     getDragData : function(e) {
43929         var target = this.findItemFromChild(e.getTarget());
43930                 if(target) {
43931                         this.handleSelection(e);
43932                         var selNodes = this.getSelectedNodes();
43933             var dragData = {
43934                 source: this,
43935                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43936                 nodes: selNodes,
43937                 records: []
43938                         };
43939                         var selectedIndices = this.getSelectedIndexes();
43940                         for (var i = 0; i < selectedIndices.length; i++) {
43941                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43942                         }
43943                         if (selNodes.length == 1) {
43944                                 dragData.ddel = target.cloneNode(true); // the div element
43945                         } else {
43946                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43947                                 div.className = 'multi-proxy';
43948                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43949                                         div.appendChild(selNodes[i].cloneNode(true));
43950                                 }
43951                                 dragData.ddel = div;
43952                         }
43953             //console.log(dragData)
43954             //console.log(dragData.ddel.innerHTML)
43955                         return dragData;
43956                 }
43957         //console.log('nodragData')
43958                 return false;
43959     },
43960     
43961 /**     Specify to which ddGroup items in this DDView may be dragged. */
43962     setDraggable: function(ddGroup) {
43963         if (ddGroup instanceof Array) {
43964                 Roo.each(ddGroup, this.setDraggable, this);
43965                 return;
43966         }
43967         if (this.dragZone) {
43968                 this.dragZone.addToGroup(ddGroup);
43969         } else {
43970                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43971                                 containerScroll: true,
43972                                 ddGroup: ddGroup 
43973
43974                         });
43975 //                      Draggability implies selection. DragZone's mousedown selects the element.
43976                         if (!this.multiSelect) { this.singleSelect = true; }
43977
43978 //                      Wire the DragZone's handlers up to methods in *this*
43979                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43980                 }
43981     },
43982
43983 /**     Specify from which ddGroup this DDView accepts drops. */
43984     setDroppable: function(ddGroup) {
43985         if (ddGroup instanceof Array) {
43986                 Roo.each(ddGroup, this.setDroppable, this);
43987                 return;
43988         }
43989         if (this.dropZone) {
43990                 this.dropZone.addToGroup(ddGroup);
43991         } else {
43992                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43993                                 containerScroll: true,
43994                                 ddGroup: ddGroup
43995                         });
43996
43997 //                      Wire the DropZone's handlers up to methods in *this*
43998                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43999                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44000                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44001                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44002                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44003                 }
44004     },
44005
44006 /**     Decide whether to drop above or below a View node. */
44007     getDropPoint : function(e, n, dd){
44008         if (n == this.el.dom) { return "above"; }
44009                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44010                 var c = t + (b - t) / 2;
44011                 var y = Roo.lib.Event.getPageY(e);
44012                 if(y <= c) {
44013                         return "above";
44014                 }else{
44015                         return "below";
44016                 }
44017     },
44018
44019     onNodeEnter : function(n, dd, e, data){
44020                 return false;
44021     },
44022     
44023     onNodeOver : function(n, dd, e, data){
44024                 var pt = this.getDropPoint(e, n, dd);
44025                 // set the insert point style on the target node
44026                 var dragElClass = this.dropNotAllowed;
44027                 if (pt) {
44028                         var targetElClass;
44029                         if (pt == "above"){
44030                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44031                                 targetElClass = "x-view-drag-insert-above";
44032                         } else {
44033                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44034                                 targetElClass = "x-view-drag-insert-below";
44035                         }
44036                         if (this.lastInsertClass != targetElClass){
44037                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44038                                 this.lastInsertClass = targetElClass;
44039                         }
44040                 }
44041                 return dragElClass;
44042         },
44043
44044     onNodeOut : function(n, dd, e, data){
44045                 this.removeDropIndicators(n);
44046     },
44047
44048     onNodeDrop : function(n, dd, e, data){
44049         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44050                 return false;
44051         }
44052         var pt = this.getDropPoint(e, n, dd);
44053                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44054                 if (pt == "below") { insertAt++; }
44055                 for (var i = 0; i < data.records.length; i++) {
44056                         var r = data.records[i];
44057                         var dup = this.store.getById(r.id);
44058                         if (dup && (dd != this.dragZone)) {
44059                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44060                         } else {
44061                                 if (data.copy) {
44062                                         this.store.insert(insertAt++, r.copy());
44063                                 } else {
44064                                         data.source.isDirtyFlag = true;
44065                                         r.store.remove(r);
44066                                         this.store.insert(insertAt++, r);
44067                                 }
44068                                 this.isDirtyFlag = true;
44069                         }
44070                 }
44071                 this.dragZone.cachedTarget = null;
44072                 return true;
44073     },
44074
44075     removeDropIndicators : function(n){
44076                 if(n){
44077                         Roo.fly(n).removeClass([
44078                                 "x-view-drag-insert-above",
44079                                 "x-view-drag-insert-below"]);
44080                         this.lastInsertClass = "_noclass";
44081                 }
44082     },
44083
44084 /**
44085  *      Utility method. Add a delete option to the DDView's context menu.
44086  *      @param {String} imageUrl The URL of the "delete" icon image.
44087  */
44088         setDeletable: function(imageUrl) {
44089                 if (!this.singleSelect && !this.multiSelect) {
44090                         this.singleSelect = true;
44091                 }
44092                 var c = this.getContextMenu();
44093                 this.contextMenu.on("itemclick", function(item) {
44094                         switch (item.id) {
44095                                 case "delete":
44096                                         this.remove(this.getSelectedIndexes());
44097                                         break;
44098                         }
44099                 }, this);
44100                 this.contextMenu.add({
44101                         icon: imageUrl,
44102                         id: "delete",
44103                         text: 'Delete'
44104                 });
44105         },
44106         
44107 /**     Return the context menu for this DDView. */
44108         getContextMenu: function() {
44109                 if (!this.contextMenu) {
44110 //                      Create the View's context menu
44111                         this.contextMenu = new Roo.menu.Menu({
44112                                 id: this.id + "-contextmenu"
44113                         });
44114                         this.el.on("contextmenu", this.showContextMenu, this);
44115                 }
44116                 return this.contextMenu;
44117         },
44118         
44119         disableContextMenu: function() {
44120                 if (this.contextMenu) {
44121                         this.el.un("contextmenu", this.showContextMenu, this);
44122                 }
44123         },
44124
44125         showContextMenu: function(e, item) {
44126         item = this.findItemFromChild(e.getTarget());
44127                 if (item) {
44128                         e.stopEvent();
44129                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44130                         this.contextMenu.showAt(e.getXY());
44131             }
44132     },
44133
44134 /**
44135  *      Remove {@link Roo.data.Record}s at the specified indices.
44136  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44137  */
44138     remove: function(selectedIndices) {
44139                 selectedIndices = [].concat(selectedIndices);
44140                 for (var i = 0; i < selectedIndices.length; i++) {
44141                         var rec = this.store.getAt(selectedIndices[i]);
44142                         this.store.remove(rec);
44143                 }
44144     },
44145
44146 /**
44147  *      Double click fires the event, but also, if this is draggable, and there is only one other
44148  *      related DropZone, it transfers the selected node.
44149  */
44150     onDblClick : function(e){
44151         var item = this.findItemFromChild(e.getTarget());
44152         if(item){
44153             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44154                 return false;
44155             }
44156             if (this.dragGroup) {
44157                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44158                     while (targets.indexOf(this.dropZone) > -1) {
44159                             targets.remove(this.dropZone);
44160                                 }
44161                     if (targets.length == 1) {
44162                                         this.dragZone.cachedTarget = null;
44163                         var el = Roo.get(targets[0].getEl());
44164                         var box = el.getBox(true);
44165                         targets[0].onNodeDrop(el.dom, {
44166                                 target: el.dom,
44167                                 xy: [box.x, box.y + box.height - 1]
44168                         }, null, this.getDragData(e));
44169                     }
44170                 }
44171         }
44172     },
44173     
44174     handleSelection: function(e) {
44175                 this.dragZone.cachedTarget = null;
44176         var item = this.findItemFromChild(e.getTarget());
44177         if (!item) {
44178                 this.clearSelections(true);
44179                 return;
44180         }
44181                 if (item && (this.multiSelect || this.singleSelect)){
44182                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44183                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44184                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44185                                 this.unselect(item);
44186                         } else {
44187                                 this.select(item, this.multiSelect && e.ctrlKey);
44188                                 this.lastSelection = item;
44189                         }
44190                 }
44191     },
44192
44193     onItemClick : function(item, index, e){
44194                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44195                         return false;
44196                 }
44197                 return true;
44198     },
44199
44200     unselect : function(nodeInfo, suppressEvent){
44201                 var node = this.getNode(nodeInfo);
44202                 if(node && this.isSelected(node)){
44203                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44204                                 Roo.fly(node).removeClass(this.selectedClass);
44205                                 this.selections.remove(node);
44206                                 if(!suppressEvent){
44207                                         this.fireEvent("selectionchange", this, this.selections);
44208                                 }
44209                         }
44210                 }
44211     }
44212 });
44213 /*
44214  * Based on:
44215  * Ext JS Library 1.1.1
44216  * Copyright(c) 2006-2007, Ext JS, LLC.
44217  *
44218  * Originally Released Under LGPL - original licence link has changed is not relivant.
44219  *
44220  * Fork - LGPL
44221  * <script type="text/javascript">
44222  */
44223  
44224 /**
44225  * @class Roo.LayoutManager
44226  * @extends Roo.util.Observable
44227  * Base class for layout managers.
44228  */
44229 Roo.LayoutManager = function(container, config){
44230     Roo.LayoutManager.superclass.constructor.call(this);
44231     this.el = Roo.get(container);
44232     // ie scrollbar fix
44233     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44234         document.body.scroll = "no";
44235     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44236         this.el.position('relative');
44237     }
44238     this.id = this.el.id;
44239     this.el.addClass("x-layout-container");
44240     /** false to disable window resize monitoring @type Boolean */
44241     this.monitorWindowResize = true;
44242     this.regions = {};
44243     this.addEvents({
44244         /**
44245          * @event layout
44246          * Fires when a layout is performed. 
44247          * @param {Roo.LayoutManager} this
44248          */
44249         "layout" : true,
44250         /**
44251          * @event regionresized
44252          * Fires when the user resizes a region. 
44253          * @param {Roo.LayoutRegion} region The resized region
44254          * @param {Number} newSize The new size (width for east/west, height for north/south)
44255          */
44256         "regionresized" : true,
44257         /**
44258          * @event regioncollapsed
44259          * Fires when a region is collapsed. 
44260          * @param {Roo.LayoutRegion} region The collapsed region
44261          */
44262         "regioncollapsed" : true,
44263         /**
44264          * @event regionexpanded
44265          * Fires when a region is expanded.  
44266          * @param {Roo.LayoutRegion} region The expanded region
44267          */
44268         "regionexpanded" : true
44269     });
44270     this.updating = false;
44271     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44272 };
44273
44274 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44275     /**
44276      * Returns true if this layout is currently being updated
44277      * @return {Boolean}
44278      */
44279     isUpdating : function(){
44280         return this.updating; 
44281     },
44282     
44283     /**
44284      * Suspend the LayoutManager from doing auto-layouts while
44285      * making multiple add or remove calls
44286      */
44287     beginUpdate : function(){
44288         this.updating = true;    
44289     },
44290     
44291     /**
44292      * Restore auto-layouts and optionally disable the manager from performing a layout
44293      * @param {Boolean} noLayout true to disable a layout update 
44294      */
44295     endUpdate : function(noLayout){
44296         this.updating = false;
44297         if(!noLayout){
44298             this.layout();
44299         }    
44300     },
44301     
44302     layout: function(){
44303         
44304     },
44305     
44306     onRegionResized : function(region, newSize){
44307         this.fireEvent("regionresized", region, newSize);
44308         this.layout();
44309     },
44310     
44311     onRegionCollapsed : function(region){
44312         this.fireEvent("regioncollapsed", region);
44313     },
44314     
44315     onRegionExpanded : function(region){
44316         this.fireEvent("regionexpanded", region);
44317     },
44318         
44319     /**
44320      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44321      * performs box-model adjustments.
44322      * @return {Object} The size as an object {width: (the width), height: (the height)}
44323      */
44324     getViewSize : function(){
44325         var size;
44326         if(this.el.dom != document.body){
44327             size = this.el.getSize();
44328         }else{
44329             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44330         }
44331         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44332         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44333         return size;
44334     },
44335     
44336     /**
44337      * Returns the Element this layout is bound to.
44338      * @return {Roo.Element}
44339      */
44340     getEl : function(){
44341         return this.el;
44342     },
44343     
44344     /**
44345      * Returns the specified region.
44346      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44347      * @return {Roo.LayoutRegion}
44348      */
44349     getRegion : function(target){
44350         return this.regions[target.toLowerCase()];
44351     },
44352     
44353     onWindowResize : function(){
44354         if(this.monitorWindowResize){
44355             this.layout();
44356         }
44357     }
44358 });/*
44359  * Based on:
44360  * Ext JS Library 1.1.1
44361  * Copyright(c) 2006-2007, Ext JS, LLC.
44362  *
44363  * Originally Released Under LGPL - original licence link has changed is not relivant.
44364  *
44365  * Fork - LGPL
44366  * <script type="text/javascript">
44367  */
44368 /**
44369  * @class Roo.BorderLayout
44370  * @extends Roo.LayoutManager
44371  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44372  * please see: <br><br>
44373  * <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>
44374  * <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>
44375  * Example:
44376  <pre><code>
44377  var layout = new Roo.BorderLayout(document.body, {
44378     north: {
44379         initialSize: 25,
44380         titlebar: false
44381     },
44382     west: {
44383         split:true,
44384         initialSize: 200,
44385         minSize: 175,
44386         maxSize: 400,
44387         titlebar: true,
44388         collapsible: true
44389     },
44390     east: {
44391         split:true,
44392         initialSize: 202,
44393         minSize: 175,
44394         maxSize: 400,
44395         titlebar: true,
44396         collapsible: true
44397     },
44398     south: {
44399         split:true,
44400         initialSize: 100,
44401         minSize: 100,
44402         maxSize: 200,
44403         titlebar: true,
44404         collapsible: true
44405     },
44406     center: {
44407         titlebar: true,
44408         autoScroll:true,
44409         resizeTabs: true,
44410         minTabWidth: 50,
44411         preferredTabWidth: 150
44412     }
44413 });
44414
44415 // shorthand
44416 var CP = Roo.ContentPanel;
44417
44418 layout.beginUpdate();
44419 layout.add("north", new CP("north", "North"));
44420 layout.add("south", new CP("south", {title: "South", closable: true}));
44421 layout.add("west", new CP("west", {title: "West"}));
44422 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44423 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44424 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44425 layout.getRegion("center").showPanel("center1");
44426 layout.endUpdate();
44427 </code></pre>
44428
44429 <b>The container the layout is rendered into can be either the body element or any other element.
44430 If it is not the body element, the container needs to either be an absolute positioned element,
44431 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44432 the container size if it is not the body element.</b>
44433
44434 * @constructor
44435 * Create a new BorderLayout
44436 * @param {String/HTMLElement/Element} container The container this layout is bound to
44437 * @param {Object} config Configuration options
44438  */
44439 Roo.BorderLayout = function(container, config){
44440     config = config || {};
44441     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44442     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44443     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44444         var target = this.factory.validRegions[i];
44445         if(config[target]){
44446             this.addRegion(target, config[target]);
44447         }
44448     }
44449 };
44450
44451 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44452     /**
44453      * Creates and adds a new region if it doesn't already exist.
44454      * @param {String} target The target region key (north, south, east, west or center).
44455      * @param {Object} config The regions config object
44456      * @return {BorderLayoutRegion} The new region
44457      */
44458     addRegion : function(target, config){
44459         if(!this.regions[target]){
44460             var r = this.factory.create(target, this, config);
44461             this.bindRegion(target, r);
44462         }
44463         return this.regions[target];
44464     },
44465
44466     // private (kinda)
44467     bindRegion : function(name, r){
44468         this.regions[name] = r;
44469         r.on("visibilitychange", this.layout, this);
44470         r.on("paneladded", this.layout, this);
44471         r.on("panelremoved", this.layout, this);
44472         r.on("invalidated", this.layout, this);
44473         r.on("resized", this.onRegionResized, this);
44474         r.on("collapsed", this.onRegionCollapsed, this);
44475         r.on("expanded", this.onRegionExpanded, this);
44476     },
44477
44478     /**
44479      * Performs a layout update.
44480      */
44481     layout : function(){
44482         if(this.updating) return;
44483         var size = this.getViewSize();
44484         var w = size.width;
44485         var h = size.height;
44486         var centerW = w;
44487         var centerH = h;
44488         var centerY = 0;
44489         var centerX = 0;
44490         //var x = 0, y = 0;
44491
44492         var rs = this.regions;
44493         var north = rs["north"];
44494         var south = rs["south"]; 
44495         var west = rs["west"];
44496         var east = rs["east"];
44497         var center = rs["center"];
44498         //if(this.hideOnLayout){ // not supported anymore
44499             //c.el.setStyle("display", "none");
44500         //}
44501         if(north && north.isVisible()){
44502             var b = north.getBox();
44503             var m = north.getMargins();
44504             b.width = w - (m.left+m.right);
44505             b.x = m.left;
44506             b.y = m.top;
44507             centerY = b.height + b.y + m.bottom;
44508             centerH -= centerY;
44509             north.updateBox(this.safeBox(b));
44510         }
44511         if(south && south.isVisible()){
44512             var b = south.getBox();
44513             var m = south.getMargins();
44514             b.width = w - (m.left+m.right);
44515             b.x = m.left;
44516             var totalHeight = (b.height + m.top + m.bottom);
44517             b.y = h - totalHeight + m.top;
44518             centerH -= totalHeight;
44519             south.updateBox(this.safeBox(b));
44520         }
44521         if(west && west.isVisible()){
44522             var b = west.getBox();
44523             var m = west.getMargins();
44524             b.height = centerH - (m.top+m.bottom);
44525             b.x = m.left;
44526             b.y = centerY + m.top;
44527             var totalWidth = (b.width + m.left + m.right);
44528             centerX += totalWidth;
44529             centerW -= totalWidth;
44530             west.updateBox(this.safeBox(b));
44531         }
44532         if(east && east.isVisible()){
44533             var b = east.getBox();
44534             var m = east.getMargins();
44535             b.height = centerH - (m.top+m.bottom);
44536             var totalWidth = (b.width + m.left + m.right);
44537             b.x = w - totalWidth + m.left;
44538             b.y = centerY + m.top;
44539             centerW -= totalWidth;
44540             east.updateBox(this.safeBox(b));
44541         }
44542         if(center){
44543             var m = center.getMargins();
44544             var centerBox = {
44545                 x: centerX + m.left,
44546                 y: centerY + m.top,
44547                 width: centerW - (m.left+m.right),
44548                 height: centerH - (m.top+m.bottom)
44549             };
44550             //if(this.hideOnLayout){
44551                 //center.el.setStyle("display", "block");
44552             //}
44553             center.updateBox(this.safeBox(centerBox));
44554         }
44555         this.el.repaint();
44556         this.fireEvent("layout", this);
44557     },
44558
44559     // private
44560     safeBox : function(box){
44561         box.width = Math.max(0, box.width);
44562         box.height = Math.max(0, box.height);
44563         return box;
44564     },
44565
44566     /**
44567      * Adds a ContentPanel (or subclass) to this layout.
44568      * @param {String} target The target region key (north, south, east, west or center).
44569      * @param {Roo.ContentPanel} panel The panel to add
44570      * @return {Roo.ContentPanel} The added panel
44571      */
44572     add : function(target, panel){
44573          
44574         target = target.toLowerCase();
44575         return this.regions[target].add(panel);
44576     },
44577
44578     /**
44579      * Remove a ContentPanel (or subclass) to this layout.
44580      * @param {String} target The target region key (north, south, east, west or center).
44581      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44582      * @return {Roo.ContentPanel} The removed panel
44583      */
44584     remove : function(target, panel){
44585         target = target.toLowerCase();
44586         return this.regions[target].remove(panel);
44587     },
44588
44589     /**
44590      * Searches all regions for a panel with the specified id
44591      * @param {String} panelId
44592      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44593      */
44594     findPanel : function(panelId){
44595         var rs = this.regions;
44596         for(var target in rs){
44597             if(typeof rs[target] != "function"){
44598                 var p = rs[target].getPanel(panelId);
44599                 if(p){
44600                     return p;
44601                 }
44602             }
44603         }
44604         return null;
44605     },
44606
44607     /**
44608      * Searches all regions for a panel with the specified id and activates (shows) it.
44609      * @param {String/ContentPanel} panelId The panels id or the panel itself
44610      * @return {Roo.ContentPanel} The shown panel or null
44611      */
44612     showPanel : function(panelId) {
44613       var rs = this.regions;
44614       for(var target in rs){
44615          var r = rs[target];
44616          if(typeof r != "function"){
44617             if(r.hasPanel(panelId)){
44618                return r.showPanel(panelId);
44619             }
44620          }
44621       }
44622       return null;
44623    },
44624
44625    /**
44626      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44627      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44628      */
44629     restoreState : function(provider){
44630         if(!provider){
44631             provider = Roo.state.Manager;
44632         }
44633         var sm = new Roo.LayoutStateManager();
44634         sm.init(this, provider);
44635     },
44636
44637     /**
44638      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44639      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44640      * a valid ContentPanel config object.  Example:
44641      * <pre><code>
44642 // Create the main layout
44643 var layout = new Roo.BorderLayout('main-ct', {
44644     west: {
44645         split:true,
44646         minSize: 175,
44647         titlebar: true
44648     },
44649     center: {
44650         title:'Components'
44651     }
44652 }, 'main-ct');
44653
44654 // Create and add multiple ContentPanels at once via configs
44655 layout.batchAdd({
44656    west: {
44657        id: 'source-files',
44658        autoCreate:true,
44659        title:'Ext Source Files',
44660        autoScroll:true,
44661        fitToFrame:true
44662    },
44663    center : {
44664        el: cview,
44665        autoScroll:true,
44666        fitToFrame:true,
44667        toolbar: tb,
44668        resizeEl:'cbody'
44669    }
44670 });
44671 </code></pre>
44672      * @param {Object} regions An object containing ContentPanel configs by region name
44673      */
44674     batchAdd : function(regions){
44675         this.beginUpdate();
44676         for(var rname in regions){
44677             var lr = this.regions[rname];
44678             if(lr){
44679                 this.addTypedPanels(lr, regions[rname]);
44680             }
44681         }
44682         this.endUpdate();
44683     },
44684
44685     // private
44686     addTypedPanels : function(lr, ps){
44687         if(typeof ps == 'string'){
44688             lr.add(new Roo.ContentPanel(ps));
44689         }
44690         else if(ps instanceof Array){
44691             for(var i =0, len = ps.length; i < len; i++){
44692                 this.addTypedPanels(lr, ps[i]);
44693             }
44694         }
44695         else if(!ps.events){ // raw config?
44696             var el = ps.el;
44697             delete ps.el; // prevent conflict
44698             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44699         }
44700         else {  // panel object assumed!
44701             lr.add(ps);
44702         }
44703     },
44704     /**
44705      * Adds a xtype elements to the layout.
44706      * <pre><code>
44707
44708 layout.addxtype({
44709        xtype : 'ContentPanel',
44710        region: 'west',
44711        items: [ .... ]
44712    }
44713 );
44714
44715 layout.addxtype({
44716         xtype : 'NestedLayoutPanel',
44717         region: 'west',
44718         layout: {
44719            center: { },
44720            west: { }   
44721         },
44722         items : [ ... list of content panels or nested layout panels.. ]
44723    }
44724 );
44725 </code></pre>
44726      * @param {Object} cfg Xtype definition of item to add.
44727      */
44728     addxtype : function(cfg)
44729     {
44730         // basically accepts a pannel...
44731         // can accept a layout region..!?!?
44732         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44733         
44734         if (!cfg.xtype.match(/Panel$/)) {
44735             return false;
44736         }
44737         var ret = false;
44738         
44739         if (typeof(cfg.region) == 'undefined') {
44740             Roo.log("Failed to add Panel, region was not set");
44741             Roo.log(cfg);
44742             return false;
44743         }
44744         var region = cfg.region;
44745         delete cfg.region;
44746         
44747           
44748         var xitems = [];
44749         if (cfg.items) {
44750             xitems = cfg.items;
44751             delete cfg.items;
44752         }
44753         var nb = false;
44754         
44755         switch(cfg.xtype) 
44756         {
44757             case 'ContentPanel':  // ContentPanel (el, cfg)
44758             case 'ScrollPanel':  // ContentPanel (el, cfg)
44759                 if(cfg.autoCreate) {
44760                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44761                 } else {
44762                     var el = this.el.createChild();
44763                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44764                 }
44765                 
44766                 this.add(region, ret);
44767                 break;
44768             
44769             
44770             case 'TreePanel': // our new panel!
44771                 cfg.el = this.el.createChild();
44772                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44773                 this.add(region, ret);
44774                 break;
44775             
44776             case 'NestedLayoutPanel': 
44777                 // create a new Layout (which is  a Border Layout...
44778                 var el = this.el.createChild();
44779                 var clayout = cfg.layout;
44780                 delete cfg.layout;
44781                 clayout.items   = clayout.items  || [];
44782                 // replace this exitems with the clayout ones..
44783                 xitems = clayout.items;
44784                  
44785                 
44786                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44787                     cfg.background = false;
44788                 }
44789                 var layout = new Roo.BorderLayout(el, clayout);
44790                 
44791                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44792                 //console.log('adding nested layout panel '  + cfg.toSource());
44793                 this.add(region, ret);
44794                 nb = {}; /// find first...
44795                 break;
44796                 
44797             case 'GridPanel': 
44798             
44799                 // needs grid and region
44800                 
44801                 //var el = this.getRegion(region).el.createChild();
44802                 var el = this.el.createChild();
44803                 // create the grid first...
44804                 
44805                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44806                 delete cfg.grid;
44807                 if (region == 'center' && this.active ) {
44808                     cfg.background = false;
44809                 }
44810                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44811                 
44812                 this.add(region, ret);
44813                 if (cfg.background) {
44814                     ret.on('activate', function(gp) {
44815                         if (!gp.grid.rendered) {
44816                             gp.grid.render();
44817                         }
44818                     });
44819                 } else {
44820                     grid.render();
44821                 }
44822                 break;
44823            
44824                
44825                 
44826                 
44827             default: 
44828                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44829                 return null;
44830              // GridPanel (grid, cfg)
44831             
44832         }
44833         this.beginUpdate();
44834         // add children..
44835         var region = '';
44836         var abn = {};
44837         Roo.each(xitems, function(i)  {
44838             region = nb && i.region ? i.region : false;
44839             
44840             var add = ret.addxtype(i);
44841            
44842             if (region) {
44843                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44844                 if (!i.background) {
44845                     abn[region] = nb[region] ;
44846                 }
44847             }
44848             
44849         });
44850         this.endUpdate();
44851
44852         // make the last non-background panel active..
44853         //if (nb) { Roo.log(abn); }
44854         if (nb) {
44855             
44856             for(var r in abn) {
44857                 region = this.getRegion(r);
44858                 if (region) {
44859                     // tried using nb[r], but it does not work..
44860                      
44861                     region.showPanel(abn[r]);
44862                    
44863                 }
44864             }
44865         }
44866         return ret;
44867         
44868     }
44869 });
44870
44871 /**
44872  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44873  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44874  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44875  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44876  * <pre><code>
44877 // shorthand
44878 var CP = Roo.ContentPanel;
44879
44880 var layout = Roo.BorderLayout.create({
44881     north: {
44882         initialSize: 25,
44883         titlebar: false,
44884         panels: [new CP("north", "North")]
44885     },
44886     west: {
44887         split:true,
44888         initialSize: 200,
44889         minSize: 175,
44890         maxSize: 400,
44891         titlebar: true,
44892         collapsible: true,
44893         panels: [new CP("west", {title: "West"})]
44894     },
44895     east: {
44896         split:true,
44897         initialSize: 202,
44898         minSize: 175,
44899         maxSize: 400,
44900         titlebar: true,
44901         collapsible: true,
44902         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44903     },
44904     south: {
44905         split:true,
44906         initialSize: 100,
44907         minSize: 100,
44908         maxSize: 200,
44909         titlebar: true,
44910         collapsible: true,
44911         panels: [new CP("south", {title: "South", closable: true})]
44912     },
44913     center: {
44914         titlebar: true,
44915         autoScroll:true,
44916         resizeTabs: true,
44917         minTabWidth: 50,
44918         preferredTabWidth: 150,
44919         panels: [
44920             new CP("center1", {title: "Close Me", closable: true}),
44921             new CP("center2", {title: "Center Panel", closable: false})
44922         ]
44923     }
44924 }, document.body);
44925
44926 layout.getRegion("center").showPanel("center1");
44927 </code></pre>
44928  * @param config
44929  * @param targetEl
44930  */
44931 Roo.BorderLayout.create = function(config, targetEl){
44932     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44933     layout.beginUpdate();
44934     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44935     for(var j = 0, jlen = regions.length; j < jlen; j++){
44936         var lr = regions[j];
44937         if(layout.regions[lr] && config[lr].panels){
44938             var r = layout.regions[lr];
44939             var ps = config[lr].panels;
44940             layout.addTypedPanels(r, ps);
44941         }
44942     }
44943     layout.endUpdate();
44944     return layout;
44945 };
44946
44947 // private
44948 Roo.BorderLayout.RegionFactory = {
44949     // private
44950     validRegions : ["north","south","east","west","center"],
44951
44952     // private
44953     create : function(target, mgr, config){
44954         target = target.toLowerCase();
44955         if(config.lightweight || config.basic){
44956             return new Roo.BasicLayoutRegion(mgr, config, target);
44957         }
44958         switch(target){
44959             case "north":
44960                 return new Roo.NorthLayoutRegion(mgr, config);
44961             case "south":
44962                 return new Roo.SouthLayoutRegion(mgr, config);
44963             case "east":
44964                 return new Roo.EastLayoutRegion(mgr, config);
44965             case "west":
44966                 return new Roo.WestLayoutRegion(mgr, config);
44967             case "center":
44968                 return new Roo.CenterLayoutRegion(mgr, config);
44969         }
44970         throw 'Layout region "'+target+'" not supported.';
44971     }
44972 };/*
44973  * Based on:
44974  * Ext JS Library 1.1.1
44975  * Copyright(c) 2006-2007, Ext JS, LLC.
44976  *
44977  * Originally Released Under LGPL - original licence link has changed is not relivant.
44978  *
44979  * Fork - LGPL
44980  * <script type="text/javascript">
44981  */
44982  
44983 /**
44984  * @class Roo.BasicLayoutRegion
44985  * @extends Roo.util.Observable
44986  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44987  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44988  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44989  */
44990 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44991     this.mgr = mgr;
44992     this.position  = pos;
44993     this.events = {
44994         /**
44995          * @scope Roo.BasicLayoutRegion
44996          */
44997         
44998         /**
44999          * @event beforeremove
45000          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45001          * @param {Roo.LayoutRegion} this
45002          * @param {Roo.ContentPanel} panel The panel
45003          * @param {Object} e The cancel event object
45004          */
45005         "beforeremove" : true,
45006         /**
45007          * @event invalidated
45008          * Fires when the layout for this region is changed.
45009          * @param {Roo.LayoutRegion} this
45010          */
45011         "invalidated" : true,
45012         /**
45013          * @event visibilitychange
45014          * Fires when this region is shown or hidden 
45015          * @param {Roo.LayoutRegion} this
45016          * @param {Boolean} visibility true or false
45017          */
45018         "visibilitychange" : true,
45019         /**
45020          * @event paneladded
45021          * Fires when a panel is added. 
45022          * @param {Roo.LayoutRegion} this
45023          * @param {Roo.ContentPanel} panel The panel
45024          */
45025         "paneladded" : true,
45026         /**
45027          * @event panelremoved
45028          * Fires when a panel is removed. 
45029          * @param {Roo.LayoutRegion} this
45030          * @param {Roo.ContentPanel} panel The panel
45031          */
45032         "panelremoved" : true,
45033         /**
45034          * @event collapsed
45035          * Fires when this region is collapsed.
45036          * @param {Roo.LayoutRegion} this
45037          */
45038         "collapsed" : true,
45039         /**
45040          * @event expanded
45041          * Fires when this region is expanded.
45042          * @param {Roo.LayoutRegion} this
45043          */
45044         "expanded" : true,
45045         /**
45046          * @event slideshow
45047          * Fires when this region is slid into view.
45048          * @param {Roo.LayoutRegion} this
45049          */
45050         "slideshow" : true,
45051         /**
45052          * @event slidehide
45053          * Fires when this region slides out of view. 
45054          * @param {Roo.LayoutRegion} this
45055          */
45056         "slidehide" : true,
45057         /**
45058          * @event panelactivated
45059          * Fires when a panel is activated. 
45060          * @param {Roo.LayoutRegion} this
45061          * @param {Roo.ContentPanel} panel The activated panel
45062          */
45063         "panelactivated" : true,
45064         /**
45065          * @event resized
45066          * Fires when the user resizes this region. 
45067          * @param {Roo.LayoutRegion} this
45068          * @param {Number} newSize The new size (width for east/west, height for north/south)
45069          */
45070         "resized" : true
45071     };
45072     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45073     this.panels = new Roo.util.MixedCollection();
45074     this.panels.getKey = this.getPanelId.createDelegate(this);
45075     this.box = null;
45076     this.activePanel = null;
45077     // ensure listeners are added...
45078     
45079     if (config.listeners || config.events) {
45080         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45081             listeners : config.listeners || {},
45082             events : config.events || {}
45083         });
45084     }
45085     
45086     if(skipConfig !== true){
45087         this.applyConfig(config);
45088     }
45089 };
45090
45091 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45092     getPanelId : function(p){
45093         return p.getId();
45094     },
45095     
45096     applyConfig : function(config){
45097         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45098         this.config = config;
45099         
45100     },
45101     
45102     /**
45103      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45104      * the width, for horizontal (north, south) the height.
45105      * @param {Number} newSize The new width or height
45106      */
45107     resizeTo : function(newSize){
45108         var el = this.el ? this.el :
45109                  (this.activePanel ? this.activePanel.getEl() : null);
45110         if(el){
45111             switch(this.position){
45112                 case "east":
45113                 case "west":
45114                     el.setWidth(newSize);
45115                     this.fireEvent("resized", this, newSize);
45116                 break;
45117                 case "north":
45118                 case "south":
45119                     el.setHeight(newSize);
45120                     this.fireEvent("resized", this, newSize);
45121                 break;                
45122             }
45123         }
45124     },
45125     
45126     getBox : function(){
45127         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45128     },
45129     
45130     getMargins : function(){
45131         return this.margins;
45132     },
45133     
45134     updateBox : function(box){
45135         this.box = box;
45136         var el = this.activePanel.getEl();
45137         el.dom.style.left = box.x + "px";
45138         el.dom.style.top = box.y + "px";
45139         this.activePanel.setSize(box.width, box.height);
45140     },
45141     
45142     /**
45143      * Returns the container element for this region.
45144      * @return {Roo.Element}
45145      */
45146     getEl : function(){
45147         return this.activePanel;
45148     },
45149     
45150     /**
45151      * Returns true if this region is currently visible.
45152      * @return {Boolean}
45153      */
45154     isVisible : function(){
45155         return this.activePanel ? true : false;
45156     },
45157     
45158     setActivePanel : function(panel){
45159         panel = this.getPanel(panel);
45160         if(this.activePanel && this.activePanel != panel){
45161             this.activePanel.setActiveState(false);
45162             this.activePanel.getEl().setLeftTop(-10000,-10000);
45163         }
45164         this.activePanel = panel;
45165         panel.setActiveState(true);
45166         if(this.box){
45167             panel.setSize(this.box.width, this.box.height);
45168         }
45169         this.fireEvent("panelactivated", this, panel);
45170         this.fireEvent("invalidated");
45171     },
45172     
45173     /**
45174      * Show the specified panel.
45175      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45176      * @return {Roo.ContentPanel} The shown panel or null
45177      */
45178     showPanel : function(panel){
45179         if(panel = this.getPanel(panel)){
45180             this.setActivePanel(panel);
45181         }
45182         return panel;
45183     },
45184     
45185     /**
45186      * Get the active panel for this region.
45187      * @return {Roo.ContentPanel} The active panel or null
45188      */
45189     getActivePanel : function(){
45190         return this.activePanel;
45191     },
45192     
45193     /**
45194      * Add the passed ContentPanel(s)
45195      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45196      * @return {Roo.ContentPanel} The panel added (if only one was added)
45197      */
45198     add : function(panel){
45199         if(arguments.length > 1){
45200             for(var i = 0, len = arguments.length; i < len; i++) {
45201                 this.add(arguments[i]);
45202             }
45203             return null;
45204         }
45205         if(this.hasPanel(panel)){
45206             this.showPanel(panel);
45207             return panel;
45208         }
45209         var el = panel.getEl();
45210         if(el.dom.parentNode != this.mgr.el.dom){
45211             this.mgr.el.dom.appendChild(el.dom);
45212         }
45213         if(panel.setRegion){
45214             panel.setRegion(this);
45215         }
45216         this.panels.add(panel);
45217         el.setStyle("position", "absolute");
45218         if(!panel.background){
45219             this.setActivePanel(panel);
45220             if(this.config.initialSize && this.panels.getCount()==1){
45221                 this.resizeTo(this.config.initialSize);
45222             }
45223         }
45224         this.fireEvent("paneladded", this, panel);
45225         return panel;
45226     },
45227     
45228     /**
45229      * Returns true if the panel is in this region.
45230      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45231      * @return {Boolean}
45232      */
45233     hasPanel : function(panel){
45234         if(typeof panel == "object"){ // must be panel obj
45235             panel = panel.getId();
45236         }
45237         return this.getPanel(panel) ? true : false;
45238     },
45239     
45240     /**
45241      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45242      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45243      * @param {Boolean} preservePanel Overrides the config preservePanel option
45244      * @return {Roo.ContentPanel} The panel that was removed
45245      */
45246     remove : function(panel, preservePanel){
45247         panel = this.getPanel(panel);
45248         if(!panel){
45249             return null;
45250         }
45251         var e = {};
45252         this.fireEvent("beforeremove", this, panel, e);
45253         if(e.cancel === true){
45254             return null;
45255         }
45256         var panelId = panel.getId();
45257         this.panels.removeKey(panelId);
45258         return panel;
45259     },
45260     
45261     /**
45262      * Returns the panel specified or null if it's not in this region.
45263      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45264      * @return {Roo.ContentPanel}
45265      */
45266     getPanel : function(id){
45267         if(typeof id == "object"){ // must be panel obj
45268             return id;
45269         }
45270         return this.panels.get(id);
45271     },
45272     
45273     /**
45274      * Returns this regions position (north/south/east/west/center).
45275      * @return {String} 
45276      */
45277     getPosition: function(){
45278         return this.position;    
45279     }
45280 });/*
45281  * Based on:
45282  * Ext JS Library 1.1.1
45283  * Copyright(c) 2006-2007, Ext JS, LLC.
45284  *
45285  * Originally Released Under LGPL - original licence link has changed is not relivant.
45286  *
45287  * Fork - LGPL
45288  * <script type="text/javascript">
45289  */
45290  
45291 /**
45292  * @class Roo.LayoutRegion
45293  * @extends Roo.BasicLayoutRegion
45294  * This class represents a region in a layout manager.
45295  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45296  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45297  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45298  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45299  * @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})
45300  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45301  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45302  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45303  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45304  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45305  * @cfg {String}    title           The title for the region (overrides panel titles)
45306  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45307  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45308  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45309  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45310  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45311  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45312  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45313  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45314  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45315  * @cfg {Boolean}   showPin         True to show a pin button
45316  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45317  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45318  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45319  * @cfg {Number}    width           For East/West panels
45320  * @cfg {Number}    height          For North/South panels
45321  * @cfg {Boolean}   split           To show the splitter
45322  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45323  */
45324 Roo.LayoutRegion = function(mgr, config, pos){
45325     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45326     var dh = Roo.DomHelper;
45327     /** This region's container element 
45328     * @type Roo.Element */
45329     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45330     /** This region's title element 
45331     * @type Roo.Element */
45332
45333     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45334         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45335         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45336     ]}, true);
45337     this.titleEl.enableDisplayMode();
45338     /** This region's title text element 
45339     * @type HTMLElement */
45340     this.titleTextEl = this.titleEl.dom.firstChild;
45341     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45342     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45343     this.closeBtn.enableDisplayMode();
45344     this.closeBtn.on("click", this.closeClicked, this);
45345     this.closeBtn.hide();
45346
45347     this.createBody(config);
45348     this.visible = true;
45349     this.collapsed = false;
45350
45351     if(config.hideWhenEmpty){
45352         this.hide();
45353         this.on("paneladded", this.validateVisibility, this);
45354         this.on("panelremoved", this.validateVisibility, this);
45355     }
45356     this.applyConfig(config);
45357 };
45358
45359 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45360
45361     createBody : function(){
45362         /** This region's body element 
45363         * @type Roo.Element */
45364         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45365     },
45366
45367     applyConfig : function(c){
45368         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45369             var dh = Roo.DomHelper;
45370             if(c.titlebar !== false){
45371                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45372                 this.collapseBtn.on("click", this.collapse, this);
45373                 this.collapseBtn.enableDisplayMode();
45374
45375                 if(c.showPin === true || this.showPin){
45376                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45377                     this.stickBtn.enableDisplayMode();
45378                     this.stickBtn.on("click", this.expand, this);
45379                     this.stickBtn.hide();
45380                 }
45381             }
45382             /** This region's collapsed element
45383             * @type Roo.Element */
45384             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45385                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45386             ]}, true);
45387             if(c.floatable !== false){
45388                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45389                this.collapsedEl.on("click", this.collapseClick, this);
45390             }
45391
45392             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45393                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45394                    id: "message", unselectable: "on", style:{"float":"left"}});
45395                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45396              }
45397             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45398             this.expandBtn.on("click", this.expand, this);
45399         }
45400         if(this.collapseBtn){
45401             this.collapseBtn.setVisible(c.collapsible == true);
45402         }
45403         this.cmargins = c.cmargins || this.cmargins ||
45404                          (this.position == "west" || this.position == "east" ?
45405                              {top: 0, left: 2, right:2, bottom: 0} :
45406                              {top: 2, left: 0, right:0, bottom: 2});
45407         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45408         this.bottomTabs = c.tabPosition != "top";
45409         this.autoScroll = c.autoScroll || false;
45410         if(this.autoScroll){
45411             this.bodyEl.setStyle("overflow", "auto");
45412         }else{
45413             this.bodyEl.setStyle("overflow", "hidden");
45414         }
45415         //if(c.titlebar !== false){
45416             if((!c.titlebar && !c.title) || c.titlebar === false){
45417                 this.titleEl.hide();
45418             }else{
45419                 this.titleEl.show();
45420                 if(c.title){
45421                     this.titleTextEl.innerHTML = c.title;
45422                 }
45423             }
45424         //}
45425         this.duration = c.duration || .30;
45426         this.slideDuration = c.slideDuration || .45;
45427         this.config = c;
45428         if(c.collapsed){
45429             this.collapse(true);
45430         }
45431         if(c.hidden){
45432             this.hide();
45433         }
45434     },
45435     /**
45436      * Returns true if this region is currently visible.
45437      * @return {Boolean}
45438      */
45439     isVisible : function(){
45440         return this.visible;
45441     },
45442
45443     /**
45444      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45445      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45446      */
45447     setCollapsedTitle : function(title){
45448         title = title || "&#160;";
45449         if(this.collapsedTitleTextEl){
45450             this.collapsedTitleTextEl.innerHTML = title;
45451         }
45452     },
45453
45454     getBox : function(){
45455         var b;
45456         if(!this.collapsed){
45457             b = this.el.getBox(false, true);
45458         }else{
45459             b = this.collapsedEl.getBox(false, true);
45460         }
45461         return b;
45462     },
45463
45464     getMargins : function(){
45465         return this.collapsed ? this.cmargins : this.margins;
45466     },
45467
45468     highlight : function(){
45469         this.el.addClass("x-layout-panel-dragover");
45470     },
45471
45472     unhighlight : function(){
45473         this.el.removeClass("x-layout-panel-dragover");
45474     },
45475
45476     updateBox : function(box){
45477         this.box = box;
45478         if(!this.collapsed){
45479             this.el.dom.style.left = box.x + "px";
45480             this.el.dom.style.top = box.y + "px";
45481             this.updateBody(box.width, box.height);
45482         }else{
45483             this.collapsedEl.dom.style.left = box.x + "px";
45484             this.collapsedEl.dom.style.top = box.y + "px";
45485             this.collapsedEl.setSize(box.width, box.height);
45486         }
45487         if(this.tabs){
45488             this.tabs.autoSizeTabs();
45489         }
45490     },
45491
45492     updateBody : function(w, h){
45493         if(w !== null){
45494             this.el.setWidth(w);
45495             w -= this.el.getBorderWidth("rl");
45496             if(this.config.adjustments){
45497                 w += this.config.adjustments[0];
45498             }
45499         }
45500         if(h !== null){
45501             this.el.setHeight(h);
45502             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45503             h -= this.el.getBorderWidth("tb");
45504             if(this.config.adjustments){
45505                 h += this.config.adjustments[1];
45506             }
45507             this.bodyEl.setHeight(h);
45508             if(this.tabs){
45509                 h = this.tabs.syncHeight(h);
45510             }
45511         }
45512         if(this.panelSize){
45513             w = w !== null ? w : this.panelSize.width;
45514             h = h !== null ? h : this.panelSize.height;
45515         }
45516         if(this.activePanel){
45517             var el = this.activePanel.getEl();
45518             w = w !== null ? w : el.getWidth();
45519             h = h !== null ? h : el.getHeight();
45520             this.panelSize = {width: w, height: h};
45521             this.activePanel.setSize(w, h);
45522         }
45523         if(Roo.isIE && this.tabs){
45524             this.tabs.el.repaint();
45525         }
45526     },
45527
45528     /**
45529      * Returns the container element for this region.
45530      * @return {Roo.Element}
45531      */
45532     getEl : function(){
45533         return this.el;
45534     },
45535
45536     /**
45537      * Hides this region.
45538      */
45539     hide : function(){
45540         if(!this.collapsed){
45541             this.el.dom.style.left = "-2000px";
45542             this.el.hide();
45543         }else{
45544             this.collapsedEl.dom.style.left = "-2000px";
45545             this.collapsedEl.hide();
45546         }
45547         this.visible = false;
45548         this.fireEvent("visibilitychange", this, false);
45549     },
45550
45551     /**
45552      * Shows this region if it was previously hidden.
45553      */
45554     show : function(){
45555         if(!this.collapsed){
45556             this.el.show();
45557         }else{
45558             this.collapsedEl.show();
45559         }
45560         this.visible = true;
45561         this.fireEvent("visibilitychange", this, true);
45562     },
45563
45564     closeClicked : function(){
45565         if(this.activePanel){
45566             this.remove(this.activePanel);
45567         }
45568     },
45569
45570     collapseClick : function(e){
45571         if(this.isSlid){
45572            e.stopPropagation();
45573            this.slideIn();
45574         }else{
45575            e.stopPropagation();
45576            this.slideOut();
45577         }
45578     },
45579
45580     /**
45581      * Collapses this region.
45582      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45583      */
45584     collapse : function(skipAnim){
45585         if(this.collapsed) return;
45586         this.collapsed = true;
45587         if(this.split){
45588             this.split.el.hide();
45589         }
45590         if(this.config.animate && skipAnim !== true){
45591             this.fireEvent("invalidated", this);
45592             this.animateCollapse();
45593         }else{
45594             this.el.setLocation(-20000,-20000);
45595             this.el.hide();
45596             this.collapsedEl.show();
45597             this.fireEvent("collapsed", this);
45598             this.fireEvent("invalidated", this);
45599         }
45600     },
45601
45602     animateCollapse : function(){
45603         // overridden
45604     },
45605
45606     /**
45607      * Expands this region if it was previously collapsed.
45608      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45609      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45610      */
45611     expand : function(e, skipAnim){
45612         if(e) e.stopPropagation();
45613         if(!this.collapsed || this.el.hasActiveFx()) return;
45614         if(this.isSlid){
45615             this.afterSlideIn();
45616             skipAnim = true;
45617         }
45618         this.collapsed = false;
45619         if(this.config.animate && skipAnim !== true){
45620             this.animateExpand();
45621         }else{
45622             this.el.show();
45623             if(this.split){
45624                 this.split.el.show();
45625             }
45626             this.collapsedEl.setLocation(-2000,-2000);
45627             this.collapsedEl.hide();
45628             this.fireEvent("invalidated", this);
45629             this.fireEvent("expanded", this);
45630         }
45631     },
45632
45633     animateExpand : function(){
45634         // overridden
45635     },
45636
45637     initTabs : function()
45638     {
45639         this.bodyEl.setStyle("overflow", "hidden");
45640         var ts = new Roo.TabPanel(
45641                 this.bodyEl.dom,
45642                 {
45643                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45644                     disableTooltips: this.config.disableTabTips,
45645                     toolbar : this.config.toolbar
45646                 }
45647         );
45648         if(this.config.hideTabs){
45649             ts.stripWrap.setDisplayed(false);
45650         }
45651         this.tabs = ts;
45652         ts.resizeTabs = this.config.resizeTabs === true;
45653         ts.minTabWidth = this.config.minTabWidth || 40;
45654         ts.maxTabWidth = this.config.maxTabWidth || 250;
45655         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45656         ts.monitorResize = false;
45657         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45658         ts.bodyEl.addClass('x-layout-tabs-body');
45659         this.panels.each(this.initPanelAsTab, this);
45660     },
45661
45662     initPanelAsTab : function(panel){
45663         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45664                     this.config.closeOnTab && panel.isClosable());
45665         if(panel.tabTip !== undefined){
45666             ti.setTooltip(panel.tabTip);
45667         }
45668         ti.on("activate", function(){
45669               this.setActivePanel(panel);
45670         }, this);
45671         if(this.config.closeOnTab){
45672             ti.on("beforeclose", function(t, e){
45673                 e.cancel = true;
45674                 this.remove(panel);
45675             }, this);
45676         }
45677         return ti;
45678     },
45679
45680     updatePanelTitle : function(panel, title){
45681         if(this.activePanel == panel){
45682             this.updateTitle(title);
45683         }
45684         if(this.tabs){
45685             var ti = this.tabs.getTab(panel.getEl().id);
45686             ti.setText(title);
45687             if(panel.tabTip !== undefined){
45688                 ti.setTooltip(panel.tabTip);
45689             }
45690         }
45691     },
45692
45693     updateTitle : function(title){
45694         if(this.titleTextEl && !this.config.title){
45695             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45696         }
45697     },
45698
45699     setActivePanel : function(panel){
45700         panel = this.getPanel(panel);
45701         if(this.activePanel && this.activePanel != panel){
45702             this.activePanel.setActiveState(false);
45703         }
45704         this.activePanel = panel;
45705         panel.setActiveState(true);
45706         if(this.panelSize){
45707             panel.setSize(this.panelSize.width, this.panelSize.height);
45708         }
45709         if(this.closeBtn){
45710             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45711         }
45712         this.updateTitle(panel.getTitle());
45713         if(this.tabs){
45714             this.fireEvent("invalidated", this);
45715         }
45716         this.fireEvent("panelactivated", this, panel);
45717     },
45718
45719     /**
45720      * Shows the specified panel.
45721      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45722      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45723      */
45724     showPanel : function(panel){
45725         if(panel = this.getPanel(panel)){
45726             if(this.tabs){
45727                 var tab = this.tabs.getTab(panel.getEl().id);
45728                 if(tab.isHidden()){
45729                     this.tabs.unhideTab(tab.id);
45730                 }
45731                 tab.activate();
45732             }else{
45733                 this.setActivePanel(panel);
45734             }
45735         }
45736         return panel;
45737     },
45738
45739     /**
45740      * Get the active panel for this region.
45741      * @return {Roo.ContentPanel} The active panel or null
45742      */
45743     getActivePanel : function(){
45744         return this.activePanel;
45745     },
45746
45747     validateVisibility : function(){
45748         if(this.panels.getCount() < 1){
45749             this.updateTitle("&#160;");
45750             this.closeBtn.hide();
45751             this.hide();
45752         }else{
45753             if(!this.isVisible()){
45754                 this.show();
45755             }
45756         }
45757     },
45758
45759     /**
45760      * Adds the passed ContentPanel(s) to this region.
45761      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45762      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45763      */
45764     add : function(panel){
45765         if(arguments.length > 1){
45766             for(var i = 0, len = arguments.length; i < len; i++) {
45767                 this.add(arguments[i]);
45768             }
45769             return null;
45770         }
45771         if(this.hasPanel(panel)){
45772             this.showPanel(panel);
45773             return panel;
45774         }
45775         panel.setRegion(this);
45776         this.panels.add(panel);
45777         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45778             this.bodyEl.dom.appendChild(panel.getEl().dom);
45779             if(panel.background !== true){
45780                 this.setActivePanel(panel);
45781             }
45782             this.fireEvent("paneladded", this, panel);
45783             return panel;
45784         }
45785         if(!this.tabs){
45786             this.initTabs();
45787         }else{
45788             this.initPanelAsTab(panel);
45789         }
45790         if(panel.background !== true){
45791             this.tabs.activate(panel.getEl().id);
45792         }
45793         this.fireEvent("paneladded", this, panel);
45794         return panel;
45795     },
45796
45797     /**
45798      * Hides the tab for the specified panel.
45799      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45800      */
45801     hidePanel : function(panel){
45802         if(this.tabs && (panel = this.getPanel(panel))){
45803             this.tabs.hideTab(panel.getEl().id);
45804         }
45805     },
45806
45807     /**
45808      * Unhides the tab for a previously hidden panel.
45809      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45810      */
45811     unhidePanel : function(panel){
45812         if(this.tabs && (panel = this.getPanel(panel))){
45813             this.tabs.unhideTab(panel.getEl().id);
45814         }
45815     },
45816
45817     clearPanels : function(){
45818         while(this.panels.getCount() > 0){
45819              this.remove(this.panels.first());
45820         }
45821     },
45822
45823     /**
45824      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45825      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45826      * @param {Boolean} preservePanel Overrides the config preservePanel option
45827      * @return {Roo.ContentPanel} The panel that was removed
45828      */
45829     remove : function(panel, preservePanel){
45830         panel = this.getPanel(panel);
45831         if(!panel){
45832             return null;
45833         }
45834         var e = {};
45835         this.fireEvent("beforeremove", this, panel, e);
45836         if(e.cancel === true){
45837             return null;
45838         }
45839         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45840         var panelId = panel.getId();
45841         this.panels.removeKey(panelId);
45842         if(preservePanel){
45843             document.body.appendChild(panel.getEl().dom);
45844         }
45845         if(this.tabs){
45846             this.tabs.removeTab(panel.getEl().id);
45847         }else if (!preservePanel){
45848             this.bodyEl.dom.removeChild(panel.getEl().dom);
45849         }
45850         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45851             var p = this.panels.first();
45852             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45853             tempEl.appendChild(p.getEl().dom);
45854             this.bodyEl.update("");
45855             this.bodyEl.dom.appendChild(p.getEl().dom);
45856             tempEl = null;
45857             this.updateTitle(p.getTitle());
45858             this.tabs = null;
45859             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45860             this.setActivePanel(p);
45861         }
45862         panel.setRegion(null);
45863         if(this.activePanel == panel){
45864             this.activePanel = null;
45865         }
45866         if(this.config.autoDestroy !== false && preservePanel !== true){
45867             try{panel.destroy();}catch(e){}
45868         }
45869         this.fireEvent("panelremoved", this, panel);
45870         return panel;
45871     },
45872
45873     /**
45874      * Returns the TabPanel component used by this region
45875      * @return {Roo.TabPanel}
45876      */
45877     getTabs : function(){
45878         return this.tabs;
45879     },
45880
45881     createTool : function(parentEl, className){
45882         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45883             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45884         btn.addClassOnOver("x-layout-tools-button-over");
45885         return btn;
45886     }
45887 });/*
45888  * Based on:
45889  * Ext JS Library 1.1.1
45890  * Copyright(c) 2006-2007, Ext JS, LLC.
45891  *
45892  * Originally Released Under LGPL - original licence link has changed is not relivant.
45893  *
45894  * Fork - LGPL
45895  * <script type="text/javascript">
45896  */
45897  
45898
45899
45900 /**
45901  * @class Roo.SplitLayoutRegion
45902  * @extends Roo.LayoutRegion
45903  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45904  */
45905 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45906     this.cursor = cursor;
45907     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45908 };
45909
45910 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45911     splitTip : "Drag to resize.",
45912     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45913     useSplitTips : false,
45914
45915     applyConfig : function(config){
45916         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45917         if(config.split){
45918             if(!this.split){
45919                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45920                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45921                 /** The SplitBar for this region 
45922                 * @type Roo.SplitBar */
45923                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45924                 this.split.on("moved", this.onSplitMove, this);
45925                 this.split.useShim = config.useShim === true;
45926                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45927                 if(this.useSplitTips){
45928                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45929                 }
45930                 if(config.collapsible){
45931                     this.split.el.on("dblclick", this.collapse,  this);
45932                 }
45933             }
45934             if(typeof config.minSize != "undefined"){
45935                 this.split.minSize = config.minSize;
45936             }
45937             if(typeof config.maxSize != "undefined"){
45938                 this.split.maxSize = config.maxSize;
45939             }
45940             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45941                 this.hideSplitter();
45942             }
45943         }
45944     },
45945
45946     getHMaxSize : function(){
45947          var cmax = this.config.maxSize || 10000;
45948          var center = this.mgr.getRegion("center");
45949          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45950     },
45951
45952     getVMaxSize : function(){
45953          var cmax = this.config.maxSize || 10000;
45954          var center = this.mgr.getRegion("center");
45955          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45956     },
45957
45958     onSplitMove : function(split, newSize){
45959         this.fireEvent("resized", this, newSize);
45960     },
45961     
45962     /** 
45963      * Returns the {@link Roo.SplitBar} for this region.
45964      * @return {Roo.SplitBar}
45965      */
45966     getSplitBar : function(){
45967         return this.split;
45968     },
45969     
45970     hide : function(){
45971         this.hideSplitter();
45972         Roo.SplitLayoutRegion.superclass.hide.call(this);
45973     },
45974
45975     hideSplitter : function(){
45976         if(this.split){
45977             this.split.el.setLocation(-2000,-2000);
45978             this.split.el.hide();
45979         }
45980     },
45981
45982     show : function(){
45983         if(this.split){
45984             this.split.el.show();
45985         }
45986         Roo.SplitLayoutRegion.superclass.show.call(this);
45987     },
45988     
45989     beforeSlide: function(){
45990         if(Roo.isGecko){// firefox overflow auto bug workaround
45991             this.bodyEl.clip();
45992             if(this.tabs) this.tabs.bodyEl.clip();
45993             if(this.activePanel){
45994                 this.activePanel.getEl().clip();
45995                 
45996                 if(this.activePanel.beforeSlide){
45997                     this.activePanel.beforeSlide();
45998                 }
45999             }
46000         }
46001     },
46002     
46003     afterSlide : function(){
46004         if(Roo.isGecko){// firefox overflow auto bug workaround
46005             this.bodyEl.unclip();
46006             if(this.tabs) this.tabs.bodyEl.unclip();
46007             if(this.activePanel){
46008                 this.activePanel.getEl().unclip();
46009                 if(this.activePanel.afterSlide){
46010                     this.activePanel.afterSlide();
46011                 }
46012             }
46013         }
46014     },
46015
46016     initAutoHide : function(){
46017         if(this.autoHide !== false){
46018             if(!this.autoHideHd){
46019                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46020                 this.autoHideHd = {
46021                     "mouseout": function(e){
46022                         if(!e.within(this.el, true)){
46023                             st.delay(500);
46024                         }
46025                     },
46026                     "mouseover" : function(e){
46027                         st.cancel();
46028                     },
46029                     scope : this
46030                 };
46031             }
46032             this.el.on(this.autoHideHd);
46033         }
46034     },
46035
46036     clearAutoHide : function(){
46037         if(this.autoHide !== false){
46038             this.el.un("mouseout", this.autoHideHd.mouseout);
46039             this.el.un("mouseover", this.autoHideHd.mouseover);
46040         }
46041     },
46042
46043     clearMonitor : function(){
46044         Roo.get(document).un("click", this.slideInIf, this);
46045     },
46046
46047     // these names are backwards but not changed for compat
46048     slideOut : function(){
46049         if(this.isSlid || this.el.hasActiveFx()){
46050             return;
46051         }
46052         this.isSlid = true;
46053         if(this.collapseBtn){
46054             this.collapseBtn.hide();
46055         }
46056         this.closeBtnState = this.closeBtn.getStyle('display');
46057         this.closeBtn.hide();
46058         if(this.stickBtn){
46059             this.stickBtn.show();
46060         }
46061         this.el.show();
46062         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46063         this.beforeSlide();
46064         this.el.setStyle("z-index", 10001);
46065         this.el.slideIn(this.getSlideAnchor(), {
46066             callback: function(){
46067                 this.afterSlide();
46068                 this.initAutoHide();
46069                 Roo.get(document).on("click", this.slideInIf, this);
46070                 this.fireEvent("slideshow", this);
46071             },
46072             scope: this,
46073             block: true
46074         });
46075     },
46076
46077     afterSlideIn : function(){
46078         this.clearAutoHide();
46079         this.isSlid = false;
46080         this.clearMonitor();
46081         this.el.setStyle("z-index", "");
46082         if(this.collapseBtn){
46083             this.collapseBtn.show();
46084         }
46085         this.closeBtn.setStyle('display', this.closeBtnState);
46086         if(this.stickBtn){
46087             this.stickBtn.hide();
46088         }
46089         this.fireEvent("slidehide", this);
46090     },
46091
46092     slideIn : function(cb){
46093         if(!this.isSlid || this.el.hasActiveFx()){
46094             Roo.callback(cb);
46095             return;
46096         }
46097         this.isSlid = false;
46098         this.beforeSlide();
46099         this.el.slideOut(this.getSlideAnchor(), {
46100             callback: function(){
46101                 this.el.setLeftTop(-10000, -10000);
46102                 this.afterSlide();
46103                 this.afterSlideIn();
46104                 Roo.callback(cb);
46105             },
46106             scope: this,
46107             block: true
46108         });
46109     },
46110     
46111     slideInIf : function(e){
46112         if(!e.within(this.el)){
46113             this.slideIn();
46114         }
46115     },
46116
46117     animateCollapse : function(){
46118         this.beforeSlide();
46119         this.el.setStyle("z-index", 20000);
46120         var anchor = this.getSlideAnchor();
46121         this.el.slideOut(anchor, {
46122             callback : function(){
46123                 this.el.setStyle("z-index", "");
46124                 this.collapsedEl.slideIn(anchor, {duration:.3});
46125                 this.afterSlide();
46126                 this.el.setLocation(-10000,-10000);
46127                 this.el.hide();
46128                 this.fireEvent("collapsed", this);
46129             },
46130             scope: this,
46131             block: true
46132         });
46133     },
46134
46135     animateExpand : function(){
46136         this.beforeSlide();
46137         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46138         this.el.setStyle("z-index", 20000);
46139         this.collapsedEl.hide({
46140             duration:.1
46141         });
46142         this.el.slideIn(this.getSlideAnchor(), {
46143             callback : function(){
46144                 this.el.setStyle("z-index", "");
46145                 this.afterSlide();
46146                 if(this.split){
46147                     this.split.el.show();
46148                 }
46149                 this.fireEvent("invalidated", this);
46150                 this.fireEvent("expanded", this);
46151             },
46152             scope: this,
46153             block: true
46154         });
46155     },
46156
46157     anchors : {
46158         "west" : "left",
46159         "east" : "right",
46160         "north" : "top",
46161         "south" : "bottom"
46162     },
46163
46164     sanchors : {
46165         "west" : "l",
46166         "east" : "r",
46167         "north" : "t",
46168         "south" : "b"
46169     },
46170
46171     canchors : {
46172         "west" : "tl-tr",
46173         "east" : "tr-tl",
46174         "north" : "tl-bl",
46175         "south" : "bl-tl"
46176     },
46177
46178     getAnchor : function(){
46179         return this.anchors[this.position];
46180     },
46181
46182     getCollapseAnchor : function(){
46183         return this.canchors[this.position];
46184     },
46185
46186     getSlideAnchor : function(){
46187         return this.sanchors[this.position];
46188     },
46189
46190     getAlignAdj : function(){
46191         var cm = this.cmargins;
46192         switch(this.position){
46193             case "west":
46194                 return [0, 0];
46195             break;
46196             case "east":
46197                 return [0, 0];
46198             break;
46199             case "north":
46200                 return [0, 0];
46201             break;
46202             case "south":
46203                 return [0, 0];
46204             break;
46205         }
46206     },
46207
46208     getExpandAdj : function(){
46209         var c = this.collapsedEl, cm = this.cmargins;
46210         switch(this.position){
46211             case "west":
46212                 return [-(cm.right+c.getWidth()+cm.left), 0];
46213             break;
46214             case "east":
46215                 return [cm.right+c.getWidth()+cm.left, 0];
46216             break;
46217             case "north":
46218                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46219             break;
46220             case "south":
46221                 return [0, cm.top+cm.bottom+c.getHeight()];
46222             break;
46223         }
46224     }
46225 });/*
46226  * Based on:
46227  * Ext JS Library 1.1.1
46228  * Copyright(c) 2006-2007, Ext JS, LLC.
46229  *
46230  * Originally Released Under LGPL - original licence link has changed is not relivant.
46231  *
46232  * Fork - LGPL
46233  * <script type="text/javascript">
46234  */
46235 /*
46236  * These classes are private internal classes
46237  */
46238 Roo.CenterLayoutRegion = function(mgr, config){
46239     Roo.LayoutRegion.call(this, mgr, config, "center");
46240     this.visible = true;
46241     this.minWidth = config.minWidth || 20;
46242     this.minHeight = config.minHeight || 20;
46243 };
46244
46245 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46246     hide : function(){
46247         // center panel can't be hidden
46248     },
46249     
46250     show : function(){
46251         // center panel can't be hidden
46252     },
46253     
46254     getMinWidth: function(){
46255         return this.minWidth;
46256     },
46257     
46258     getMinHeight: function(){
46259         return this.minHeight;
46260     }
46261 });
46262
46263
46264 Roo.NorthLayoutRegion = function(mgr, config){
46265     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46266     if(this.split){
46267         this.split.placement = Roo.SplitBar.TOP;
46268         this.split.orientation = Roo.SplitBar.VERTICAL;
46269         this.split.el.addClass("x-layout-split-v");
46270     }
46271     var size = config.initialSize || config.height;
46272     if(typeof size != "undefined"){
46273         this.el.setHeight(size);
46274     }
46275 };
46276 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46277     orientation: Roo.SplitBar.VERTICAL,
46278     getBox : function(){
46279         if(this.collapsed){
46280             return this.collapsedEl.getBox();
46281         }
46282         var box = this.el.getBox();
46283         if(this.split){
46284             box.height += this.split.el.getHeight();
46285         }
46286         return box;
46287     },
46288     
46289     updateBox : function(box){
46290         if(this.split && !this.collapsed){
46291             box.height -= this.split.el.getHeight();
46292             this.split.el.setLeft(box.x);
46293             this.split.el.setTop(box.y+box.height);
46294             this.split.el.setWidth(box.width);
46295         }
46296         if(this.collapsed){
46297             this.updateBody(box.width, null);
46298         }
46299         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46300     }
46301 });
46302
46303 Roo.SouthLayoutRegion = function(mgr, config){
46304     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46305     if(this.split){
46306         this.split.placement = Roo.SplitBar.BOTTOM;
46307         this.split.orientation = Roo.SplitBar.VERTICAL;
46308         this.split.el.addClass("x-layout-split-v");
46309     }
46310     var size = config.initialSize || config.height;
46311     if(typeof size != "undefined"){
46312         this.el.setHeight(size);
46313     }
46314 };
46315 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46316     orientation: Roo.SplitBar.VERTICAL,
46317     getBox : function(){
46318         if(this.collapsed){
46319             return this.collapsedEl.getBox();
46320         }
46321         var box = this.el.getBox();
46322         if(this.split){
46323             var sh = this.split.el.getHeight();
46324             box.height += sh;
46325             box.y -= sh;
46326         }
46327         return box;
46328     },
46329     
46330     updateBox : function(box){
46331         if(this.split && !this.collapsed){
46332             var sh = this.split.el.getHeight();
46333             box.height -= sh;
46334             box.y += sh;
46335             this.split.el.setLeft(box.x);
46336             this.split.el.setTop(box.y-sh);
46337             this.split.el.setWidth(box.width);
46338         }
46339         if(this.collapsed){
46340             this.updateBody(box.width, null);
46341         }
46342         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46343     }
46344 });
46345
46346 Roo.EastLayoutRegion = function(mgr, config){
46347     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46348     if(this.split){
46349         this.split.placement = Roo.SplitBar.RIGHT;
46350         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46351         this.split.el.addClass("x-layout-split-h");
46352     }
46353     var size = config.initialSize || config.width;
46354     if(typeof size != "undefined"){
46355         this.el.setWidth(size);
46356     }
46357 };
46358 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46359     orientation: Roo.SplitBar.HORIZONTAL,
46360     getBox : function(){
46361         if(this.collapsed){
46362             return this.collapsedEl.getBox();
46363         }
46364         var box = this.el.getBox();
46365         if(this.split){
46366             var sw = this.split.el.getWidth();
46367             box.width += sw;
46368             box.x -= sw;
46369         }
46370         return box;
46371     },
46372
46373     updateBox : function(box){
46374         if(this.split && !this.collapsed){
46375             var sw = this.split.el.getWidth();
46376             box.width -= sw;
46377             this.split.el.setLeft(box.x);
46378             this.split.el.setTop(box.y);
46379             this.split.el.setHeight(box.height);
46380             box.x += sw;
46381         }
46382         if(this.collapsed){
46383             this.updateBody(null, box.height);
46384         }
46385         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46386     }
46387 });
46388
46389 Roo.WestLayoutRegion = function(mgr, config){
46390     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46391     if(this.split){
46392         this.split.placement = Roo.SplitBar.LEFT;
46393         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46394         this.split.el.addClass("x-layout-split-h");
46395     }
46396     var size = config.initialSize || config.width;
46397     if(typeof size != "undefined"){
46398         this.el.setWidth(size);
46399     }
46400 };
46401 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46402     orientation: Roo.SplitBar.HORIZONTAL,
46403     getBox : function(){
46404         if(this.collapsed){
46405             return this.collapsedEl.getBox();
46406         }
46407         var box = this.el.getBox();
46408         if(this.split){
46409             box.width += this.split.el.getWidth();
46410         }
46411         return box;
46412     },
46413     
46414     updateBox : function(box){
46415         if(this.split && !this.collapsed){
46416             var sw = this.split.el.getWidth();
46417             box.width -= sw;
46418             this.split.el.setLeft(box.x+box.width);
46419             this.split.el.setTop(box.y);
46420             this.split.el.setHeight(box.height);
46421         }
46422         if(this.collapsed){
46423             this.updateBody(null, box.height);
46424         }
46425         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46426     }
46427 });
46428 /*
46429  * Based on:
46430  * Ext JS Library 1.1.1
46431  * Copyright(c) 2006-2007, Ext JS, LLC.
46432  *
46433  * Originally Released Under LGPL - original licence link has changed is not relivant.
46434  *
46435  * Fork - LGPL
46436  * <script type="text/javascript">
46437  */
46438  
46439  
46440 /*
46441  * Private internal class for reading and applying state
46442  */
46443 Roo.LayoutStateManager = function(layout){
46444      // default empty state
46445      this.state = {
46446         north: {},
46447         south: {},
46448         east: {},
46449         west: {}       
46450     };
46451 };
46452
46453 Roo.LayoutStateManager.prototype = {
46454     init : function(layout, provider){
46455         this.provider = provider;
46456         var state = provider.get(layout.id+"-layout-state");
46457         if(state){
46458             var wasUpdating = layout.isUpdating();
46459             if(!wasUpdating){
46460                 layout.beginUpdate();
46461             }
46462             for(var key in state){
46463                 if(typeof state[key] != "function"){
46464                     var rstate = state[key];
46465                     var r = layout.getRegion(key);
46466                     if(r && rstate){
46467                         if(rstate.size){
46468                             r.resizeTo(rstate.size);
46469                         }
46470                         if(rstate.collapsed == true){
46471                             r.collapse(true);
46472                         }else{
46473                             r.expand(null, true);
46474                         }
46475                     }
46476                 }
46477             }
46478             if(!wasUpdating){
46479                 layout.endUpdate();
46480             }
46481             this.state = state; 
46482         }
46483         this.layout = layout;
46484         layout.on("regionresized", this.onRegionResized, this);
46485         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46486         layout.on("regionexpanded", this.onRegionExpanded, this);
46487     },
46488     
46489     storeState : function(){
46490         this.provider.set(this.layout.id+"-layout-state", this.state);
46491     },
46492     
46493     onRegionResized : function(region, newSize){
46494         this.state[region.getPosition()].size = newSize;
46495         this.storeState();
46496     },
46497     
46498     onRegionCollapsed : function(region){
46499         this.state[region.getPosition()].collapsed = true;
46500         this.storeState();
46501     },
46502     
46503     onRegionExpanded : function(region){
46504         this.state[region.getPosition()].collapsed = false;
46505         this.storeState();
46506     }
46507 };/*
46508  * Based on:
46509  * Ext JS Library 1.1.1
46510  * Copyright(c) 2006-2007, Ext JS, LLC.
46511  *
46512  * Originally Released Under LGPL - original licence link has changed is not relivant.
46513  *
46514  * Fork - LGPL
46515  * <script type="text/javascript">
46516  */
46517 /**
46518  * @class Roo.ContentPanel
46519  * @extends Roo.util.Observable
46520  * A basic ContentPanel element.
46521  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46522  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46523  * @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
46524  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46525  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46526  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46527  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46528  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46529  * @cfg {String} title          The title for this panel
46530  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46531  * @cfg {String} url            Calls {@link #setUrl} with this value
46532  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46533  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46534  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46535  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46536
46537  * @constructor
46538  * Create a new ContentPanel.
46539  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46540  * @param {String/Object} config A string to set only the title or a config object
46541  * @param {String} content (optional) Set the HTML content for this panel
46542  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46543  */
46544 Roo.ContentPanel = function(el, config, content){
46545     
46546      
46547     /*
46548     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46549         config = el;
46550         el = Roo.id();
46551     }
46552     if (config && config.parentLayout) { 
46553         el = config.parentLayout.el.createChild(); 
46554     }
46555     */
46556     if(el.autoCreate){ // xtype is available if this is called from factory
46557         config = el;
46558         el = Roo.id();
46559     }
46560     this.el = Roo.get(el);
46561     if(!this.el && config && config.autoCreate){
46562         if(typeof config.autoCreate == "object"){
46563             if(!config.autoCreate.id){
46564                 config.autoCreate.id = config.id||el;
46565             }
46566             this.el = Roo.DomHelper.append(document.body,
46567                         config.autoCreate, true);
46568         }else{
46569             this.el = Roo.DomHelper.append(document.body,
46570                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46571         }
46572     }
46573     this.closable = false;
46574     this.loaded = false;
46575     this.active = false;
46576     if(typeof config == "string"){
46577         this.title = config;
46578     }else{
46579         Roo.apply(this, config);
46580     }
46581     
46582     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46583         this.wrapEl = this.el.wrap();
46584         this.toolbar.container = this.el.insertSibling(false, 'before');
46585         this.toolbar = new Roo.Toolbar(this.toolbar);
46586     }
46587     
46588     
46589     
46590     if(this.resizeEl){
46591         this.resizeEl = Roo.get(this.resizeEl, true);
46592     }else{
46593         this.resizeEl = this.el;
46594     }
46595     this.addEvents({
46596         /**
46597          * @event activate
46598          * Fires when this panel is activated. 
46599          * @param {Roo.ContentPanel} this
46600          */
46601         "activate" : true,
46602         /**
46603          * @event deactivate
46604          * Fires when this panel is activated. 
46605          * @param {Roo.ContentPanel} this
46606          */
46607         "deactivate" : true,
46608
46609         /**
46610          * @event resize
46611          * Fires when this panel is resized if fitToFrame is true.
46612          * @param {Roo.ContentPanel} this
46613          * @param {Number} width The width after any component adjustments
46614          * @param {Number} height The height after any component adjustments
46615          */
46616         "resize" : true,
46617         
46618          /**
46619          * @event render
46620          * Fires when this tab is created
46621          * @param {Roo.ContentPanel} this
46622          */
46623         "render" : true
46624         
46625         
46626         
46627     });
46628     if(this.autoScroll){
46629         this.resizeEl.setStyle("overflow", "auto");
46630     } else {
46631         // fix randome scrolling
46632         this.el.on('scroll', function() {
46633             Roo.log('fix random scolling');
46634             this.scrollTo('top',0); 
46635         });
46636     }
46637     content = content || this.content;
46638     if(content){
46639         this.setContent(content);
46640     }
46641     if(config && config.url){
46642         this.setUrl(this.url, this.params, this.loadOnce);
46643     }
46644     
46645     
46646     
46647     Roo.ContentPanel.superclass.constructor.call(this);
46648     
46649     this.fireEvent('render', this);
46650 };
46651
46652 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46653     tabTip:'',
46654     setRegion : function(region){
46655         this.region = region;
46656         if(region){
46657            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46658         }else{
46659            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46660         } 
46661     },
46662     
46663     /**
46664      * Returns the toolbar for this Panel if one was configured. 
46665      * @return {Roo.Toolbar} 
46666      */
46667     getToolbar : function(){
46668         return this.toolbar;
46669     },
46670     
46671     setActiveState : function(active){
46672         this.active = active;
46673         if(!active){
46674             this.fireEvent("deactivate", this);
46675         }else{
46676             this.fireEvent("activate", this);
46677         }
46678     },
46679     /**
46680      * Updates this panel's element
46681      * @param {String} content The new content
46682      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46683     */
46684     setContent : function(content, loadScripts){
46685         this.el.update(content, loadScripts);
46686     },
46687
46688     ignoreResize : function(w, h){
46689         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46690             return true;
46691         }else{
46692             this.lastSize = {width: w, height: h};
46693             return false;
46694         }
46695     },
46696     /**
46697      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46698      * @return {Roo.UpdateManager} The UpdateManager
46699      */
46700     getUpdateManager : function(){
46701         return this.el.getUpdateManager();
46702     },
46703      /**
46704      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46705      * @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:
46706 <pre><code>
46707 panel.load({
46708     url: "your-url.php",
46709     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46710     callback: yourFunction,
46711     scope: yourObject, //(optional scope)
46712     discardUrl: false,
46713     nocache: false,
46714     text: "Loading...",
46715     timeout: 30,
46716     scripts: false
46717 });
46718 </code></pre>
46719      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46720      * 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.
46721      * @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}
46722      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46723      * @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.
46724      * @return {Roo.ContentPanel} this
46725      */
46726     load : function(){
46727         var um = this.el.getUpdateManager();
46728         um.update.apply(um, arguments);
46729         return this;
46730     },
46731
46732
46733     /**
46734      * 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.
46735      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46736      * @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)
46737      * @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)
46738      * @return {Roo.UpdateManager} The UpdateManager
46739      */
46740     setUrl : function(url, params, loadOnce){
46741         if(this.refreshDelegate){
46742             this.removeListener("activate", this.refreshDelegate);
46743         }
46744         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46745         this.on("activate", this.refreshDelegate);
46746         return this.el.getUpdateManager();
46747     },
46748     
46749     _handleRefresh : function(url, params, loadOnce){
46750         if(!loadOnce || !this.loaded){
46751             var updater = this.el.getUpdateManager();
46752             updater.update(url, params, this._setLoaded.createDelegate(this));
46753         }
46754     },
46755     
46756     _setLoaded : function(){
46757         this.loaded = true;
46758     }, 
46759     
46760     /**
46761      * Returns this panel's id
46762      * @return {String} 
46763      */
46764     getId : function(){
46765         return this.el.id;
46766     },
46767     
46768     /** 
46769      * Returns this panel's element - used by regiosn to add.
46770      * @return {Roo.Element} 
46771      */
46772     getEl : function(){
46773         return this.wrapEl || this.el;
46774     },
46775     
46776     adjustForComponents : function(width, height){
46777         if(this.resizeEl != this.el){
46778             width -= this.el.getFrameWidth('lr');
46779             height -= this.el.getFrameWidth('tb');
46780         }
46781         if(this.toolbar){
46782             var te = this.toolbar.getEl();
46783             height -= te.getHeight();
46784             te.setWidth(width);
46785         }
46786         if(this.adjustments){
46787             width += this.adjustments[0];
46788             height += this.adjustments[1];
46789         }
46790         return {"width": width, "height": height};
46791     },
46792     
46793     setSize : function(width, height){
46794         if(this.fitToFrame && !this.ignoreResize(width, height)){
46795             if(this.fitContainer && this.resizeEl != this.el){
46796                 this.el.setSize(width, height);
46797             }
46798             var size = this.adjustForComponents(width, height);
46799             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46800             this.fireEvent('resize', this, size.width, size.height);
46801         }
46802     },
46803     
46804     /**
46805      * Returns this panel's title
46806      * @return {String} 
46807      */
46808     getTitle : function(){
46809         return this.title;
46810     },
46811     
46812     /**
46813      * Set this panel's title
46814      * @param {String} title
46815      */
46816     setTitle : function(title){
46817         this.title = title;
46818         if(this.region){
46819             this.region.updatePanelTitle(this, title);
46820         }
46821     },
46822     
46823     /**
46824      * Returns true is this panel was configured to be closable
46825      * @return {Boolean} 
46826      */
46827     isClosable : function(){
46828         return this.closable;
46829     },
46830     
46831     beforeSlide : function(){
46832         this.el.clip();
46833         this.resizeEl.clip();
46834     },
46835     
46836     afterSlide : function(){
46837         this.el.unclip();
46838         this.resizeEl.unclip();
46839     },
46840     
46841     /**
46842      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46843      *   Will fail silently if the {@link #setUrl} method has not been called.
46844      *   This does not activate the panel, just updates its content.
46845      */
46846     refresh : function(){
46847         if(this.refreshDelegate){
46848            this.loaded = false;
46849            this.refreshDelegate();
46850         }
46851     },
46852     
46853     /**
46854      * Destroys this panel
46855      */
46856     destroy : function(){
46857         this.el.removeAllListeners();
46858         var tempEl = document.createElement("span");
46859         tempEl.appendChild(this.el.dom);
46860         tempEl.innerHTML = "";
46861         this.el.remove();
46862         this.el = null;
46863     },
46864     
46865     /**
46866      * form - if the content panel contains a form - this is a reference to it.
46867      * @type {Roo.form.Form}
46868      */
46869     form : false,
46870     /**
46871      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46872      *    This contains a reference to it.
46873      * @type {Roo.View}
46874      */
46875     view : false,
46876     
46877       /**
46878      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46879      * <pre><code>
46880
46881 layout.addxtype({
46882        xtype : 'Form',
46883        items: [ .... ]
46884    }
46885 );
46886
46887 </code></pre>
46888      * @param {Object} cfg Xtype definition of item to add.
46889      */
46890     
46891     addxtype : function(cfg) {
46892         // add form..
46893         if (cfg.xtype.match(/^Form$/)) {
46894             var el = this.el.createChild();
46895
46896             this.form = new  Roo.form.Form(cfg);
46897             
46898             
46899             if ( this.form.allItems.length) this.form.render(el.dom);
46900             return this.form;
46901         }
46902         // should only have one of theses..
46903         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46904             // views..
46905             cfg.el = this.el.appendChild(document.createElement("div"));
46906             // factory?
46907             
46908             var ret = new Roo.factory(cfg);
46909             ret.render && ret.render(false, ''); // render blank..
46910             this.view = ret;
46911             return ret;
46912         }
46913         return false;
46914     }
46915 });
46916
46917 /**
46918  * @class Roo.GridPanel
46919  * @extends Roo.ContentPanel
46920  * @constructor
46921  * Create a new GridPanel.
46922  * @param {Roo.grid.Grid} grid The grid for this panel
46923  * @param {String/Object} config A string to set only the panel's title, or a config object
46924  */
46925 Roo.GridPanel = function(grid, config){
46926     
46927   
46928     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46929         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46930         
46931     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46932     
46933     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46934     
46935     if(this.toolbar){
46936         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46937     }
46938     // xtype created footer. - not sure if will work as we normally have to render first..
46939     if (this.footer && !this.footer.el && this.footer.xtype) {
46940         
46941         this.footer.container = this.grid.getView().getFooterPanel(true);
46942         this.footer.dataSource = this.grid.dataSource;
46943         this.footer = Roo.factory(this.footer, Roo);
46944         
46945     }
46946     
46947     grid.monitorWindowResize = false; // turn off autosizing
46948     grid.autoHeight = false;
46949     grid.autoWidth = false;
46950     this.grid = grid;
46951     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46952 };
46953
46954 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46955     getId : function(){
46956         return this.grid.id;
46957     },
46958     
46959     /**
46960      * Returns the grid for this panel
46961      * @return {Roo.grid.Grid} 
46962      */
46963     getGrid : function(){
46964         return this.grid;    
46965     },
46966     
46967     setSize : function(width, height){
46968         if(!this.ignoreResize(width, height)){
46969             var grid = this.grid;
46970             var size = this.adjustForComponents(width, height);
46971             grid.getGridEl().setSize(size.width, size.height);
46972             grid.autoSize();
46973         }
46974     },
46975     
46976     beforeSlide : function(){
46977         this.grid.getView().scroller.clip();
46978     },
46979     
46980     afterSlide : function(){
46981         this.grid.getView().scroller.unclip();
46982     },
46983     
46984     destroy : function(){
46985         this.grid.destroy();
46986         delete this.grid;
46987         Roo.GridPanel.superclass.destroy.call(this); 
46988     }
46989 });
46990
46991
46992 /**
46993  * @class Roo.NestedLayoutPanel
46994  * @extends Roo.ContentPanel
46995  * @constructor
46996  * Create a new NestedLayoutPanel.
46997  * 
46998  * 
46999  * @param {Roo.BorderLayout} layout The layout for this panel
47000  * @param {String/Object} config A string to set only the title or a config object
47001  */
47002 Roo.NestedLayoutPanel = function(layout, config)
47003 {
47004     // construct with only one argument..
47005     /* FIXME - implement nicer consturctors
47006     if (layout.layout) {
47007         config = layout;
47008         layout = config.layout;
47009         delete config.layout;
47010     }
47011     if (layout.xtype && !layout.getEl) {
47012         // then layout needs constructing..
47013         layout = Roo.factory(layout, Roo);
47014     }
47015     */
47016     
47017     
47018     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47019     
47020     layout.monitorWindowResize = false; // turn off autosizing
47021     this.layout = layout;
47022     this.layout.getEl().addClass("x-layout-nested-layout");
47023     
47024     
47025     
47026     
47027 };
47028
47029 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47030
47031     setSize : function(width, height){
47032         if(!this.ignoreResize(width, height)){
47033             var size = this.adjustForComponents(width, height);
47034             var el = this.layout.getEl();
47035             el.setSize(size.width, size.height);
47036             var touch = el.dom.offsetWidth;
47037             this.layout.layout();
47038             // ie requires a double layout on the first pass
47039             if(Roo.isIE && !this.initialized){
47040                 this.initialized = true;
47041                 this.layout.layout();
47042             }
47043         }
47044     },
47045     
47046     // activate all subpanels if not currently active..
47047     
47048     setActiveState : function(active){
47049         this.active = active;
47050         if(!active){
47051             this.fireEvent("deactivate", this);
47052             return;
47053         }
47054         
47055         this.fireEvent("activate", this);
47056         // not sure if this should happen before or after..
47057         if (!this.layout) {
47058             return; // should not happen..
47059         }
47060         var reg = false;
47061         for (var r in this.layout.regions) {
47062             reg = this.layout.getRegion(r);
47063             if (reg.getActivePanel()) {
47064                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47065                 reg.setActivePanel(reg.getActivePanel());
47066                 continue;
47067             }
47068             if (!reg.panels.length) {
47069                 continue;
47070             }
47071             reg.showPanel(reg.getPanel(0));
47072         }
47073         
47074         
47075         
47076         
47077     },
47078     
47079     /**
47080      * Returns the nested BorderLayout for this panel
47081      * @return {Roo.BorderLayout} 
47082      */
47083     getLayout : function(){
47084         return this.layout;
47085     },
47086     
47087      /**
47088      * Adds a xtype elements to the layout of the nested panel
47089      * <pre><code>
47090
47091 panel.addxtype({
47092        xtype : 'ContentPanel',
47093        region: 'west',
47094        items: [ .... ]
47095    }
47096 );
47097
47098 panel.addxtype({
47099         xtype : 'NestedLayoutPanel',
47100         region: 'west',
47101         layout: {
47102            center: { },
47103            west: { }   
47104         },
47105         items : [ ... list of content panels or nested layout panels.. ]
47106    }
47107 );
47108 </code></pre>
47109      * @param {Object} cfg Xtype definition of item to add.
47110      */
47111     addxtype : function(cfg) {
47112         return this.layout.addxtype(cfg);
47113     
47114     }
47115 });
47116
47117 Roo.ScrollPanel = function(el, config, content){
47118     config = config || {};
47119     config.fitToFrame = true;
47120     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47121     
47122     this.el.dom.style.overflow = "hidden";
47123     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47124     this.el.removeClass("x-layout-inactive-content");
47125     this.el.on("mousewheel", this.onWheel, this);
47126
47127     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47128     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47129     up.unselectable(); down.unselectable();
47130     up.on("click", this.scrollUp, this);
47131     down.on("click", this.scrollDown, this);
47132     up.addClassOnOver("x-scroller-btn-over");
47133     down.addClassOnOver("x-scroller-btn-over");
47134     up.addClassOnClick("x-scroller-btn-click");
47135     down.addClassOnClick("x-scroller-btn-click");
47136     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47137
47138     this.resizeEl = this.el;
47139     this.el = wrap; this.up = up; this.down = down;
47140 };
47141
47142 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47143     increment : 100,
47144     wheelIncrement : 5,
47145     scrollUp : function(){
47146         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47147     },
47148
47149     scrollDown : function(){
47150         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47151     },
47152
47153     afterScroll : function(){
47154         var el = this.resizeEl;
47155         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47156         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47157         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47158     },
47159
47160     setSize : function(){
47161         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47162         this.afterScroll();
47163     },
47164
47165     onWheel : function(e){
47166         var d = e.getWheelDelta();
47167         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47168         this.afterScroll();
47169         e.stopEvent();
47170     },
47171
47172     setContent : function(content, loadScripts){
47173         this.resizeEl.update(content, loadScripts);
47174     }
47175
47176 });
47177
47178
47179
47180
47181
47182
47183
47184
47185
47186 /**
47187  * @class Roo.TreePanel
47188  * @extends Roo.ContentPanel
47189  * @constructor
47190  * Create a new TreePanel. - defaults to fit/scoll contents.
47191  * @param {String/Object} config A string to set only the panel's title, or a config object
47192  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47193  */
47194 Roo.TreePanel = function(config){
47195     var el = config.el;
47196     var tree = config.tree;
47197     delete config.tree; 
47198     delete config.el; // hopefull!
47199     
47200     // wrapper for IE7 strict & safari scroll issue
47201     
47202     var treeEl = el.createChild();
47203     config.resizeEl = treeEl;
47204     
47205     
47206     
47207     Roo.TreePanel.superclass.constructor.call(this, el, config);
47208  
47209  
47210     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47211     //console.log(tree);
47212     this.on('activate', function()
47213     {
47214         if (this.tree.rendered) {
47215             return;
47216         }
47217         //console.log('render tree');
47218         this.tree.render();
47219     });
47220     
47221     this.on('resize',  function (cp, w, h) {
47222             this.tree.innerCt.setWidth(w);
47223             this.tree.innerCt.setHeight(h);
47224             this.tree.innerCt.setStyle('overflow-y', 'auto');
47225     });
47226
47227         
47228     
47229 };
47230
47231 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47232     fitToFrame : true,
47233     autoScroll : true
47234 });
47235
47236
47237
47238
47239
47240
47241
47242
47243
47244
47245
47246 /*
47247  * Based on:
47248  * Ext JS Library 1.1.1
47249  * Copyright(c) 2006-2007, Ext JS, LLC.
47250  *
47251  * Originally Released Under LGPL - original licence link has changed is not relivant.
47252  *
47253  * Fork - LGPL
47254  * <script type="text/javascript">
47255  */
47256  
47257
47258 /**
47259  * @class Roo.ReaderLayout
47260  * @extends Roo.BorderLayout
47261  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47262  * center region containing two nested regions (a top one for a list view and one for item preview below),
47263  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47264  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47265  * expedites the setup of the overall layout and regions for this common application style.
47266  * Example:
47267  <pre><code>
47268 var reader = new Roo.ReaderLayout();
47269 var CP = Roo.ContentPanel;  // shortcut for adding
47270
47271 reader.beginUpdate();
47272 reader.add("north", new CP("north", "North"));
47273 reader.add("west", new CP("west", {title: "West"}));
47274 reader.add("east", new CP("east", {title: "East"}));
47275
47276 reader.regions.listView.add(new CP("listView", "List"));
47277 reader.regions.preview.add(new CP("preview", "Preview"));
47278 reader.endUpdate();
47279 </code></pre>
47280 * @constructor
47281 * Create a new ReaderLayout
47282 * @param {Object} config Configuration options
47283 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47284 * document.body if omitted)
47285 */
47286 Roo.ReaderLayout = function(config, renderTo){
47287     var c = config || {size:{}};
47288     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47289         north: c.north !== false ? Roo.apply({
47290             split:false,
47291             initialSize: 32,
47292             titlebar: false
47293         }, c.north) : false,
47294         west: c.west !== false ? Roo.apply({
47295             split:true,
47296             initialSize: 200,
47297             minSize: 175,
47298             maxSize: 400,
47299             titlebar: true,
47300             collapsible: true,
47301             animate: true,
47302             margins:{left:5,right:0,bottom:5,top:5},
47303             cmargins:{left:5,right:5,bottom:5,top:5}
47304         }, c.west) : false,
47305         east: c.east !== false ? Roo.apply({
47306             split:true,
47307             initialSize: 200,
47308             minSize: 175,
47309             maxSize: 400,
47310             titlebar: true,
47311             collapsible: true,
47312             animate: true,
47313             margins:{left:0,right:5,bottom:5,top:5},
47314             cmargins:{left:5,right:5,bottom:5,top:5}
47315         }, c.east) : false,
47316         center: Roo.apply({
47317             tabPosition: 'top',
47318             autoScroll:false,
47319             closeOnTab: true,
47320             titlebar:false,
47321             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47322         }, c.center)
47323     });
47324
47325     this.el.addClass('x-reader');
47326
47327     this.beginUpdate();
47328
47329     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47330         south: c.preview !== false ? Roo.apply({
47331             split:true,
47332             initialSize: 200,
47333             minSize: 100,
47334             autoScroll:true,
47335             collapsible:true,
47336             titlebar: true,
47337             cmargins:{top:5,left:0, right:0, bottom:0}
47338         }, c.preview) : false,
47339         center: Roo.apply({
47340             autoScroll:false,
47341             titlebar:false,
47342             minHeight:200
47343         }, c.listView)
47344     });
47345     this.add('center', new Roo.NestedLayoutPanel(inner,
47346             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47347
47348     this.endUpdate();
47349
47350     this.regions.preview = inner.getRegion('south');
47351     this.regions.listView = inner.getRegion('center');
47352 };
47353
47354 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47355  * Based on:
47356  * Ext JS Library 1.1.1
47357  * Copyright(c) 2006-2007, Ext JS, LLC.
47358  *
47359  * Originally Released Under LGPL - original licence link has changed is not relivant.
47360  *
47361  * Fork - LGPL
47362  * <script type="text/javascript">
47363  */
47364  
47365 /**
47366  * @class Roo.grid.Grid
47367  * @extends Roo.util.Observable
47368  * This class represents the primary interface of a component based grid control.
47369  * <br><br>Usage:<pre><code>
47370  var grid = new Roo.grid.Grid("my-container-id", {
47371      ds: myDataStore,
47372      cm: myColModel,
47373      selModel: mySelectionModel,
47374      autoSizeColumns: true,
47375      monitorWindowResize: false,
47376      trackMouseOver: true
47377  });
47378  // set any options
47379  grid.render();
47380  * </code></pre>
47381  * <b>Common Problems:</b><br/>
47382  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47383  * element will correct this<br/>
47384  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47385  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47386  * are unpredictable.<br/>
47387  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47388  * grid to calculate dimensions/offsets.<br/>
47389   * @constructor
47390  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47391  * The container MUST have some type of size defined for the grid to fill. The container will be
47392  * automatically set to position relative if it isn't already.
47393  * @param {Object} config A config object that sets properties on this grid.
47394  */
47395 Roo.grid.Grid = function(container, config){
47396         // initialize the container
47397         this.container = Roo.get(container);
47398         this.container.update("");
47399         this.container.setStyle("overflow", "hidden");
47400     this.container.addClass('x-grid-container');
47401
47402     this.id = this.container.id;
47403
47404     Roo.apply(this, config);
47405     // check and correct shorthanded configs
47406     if(this.ds){
47407         this.dataSource = this.ds;
47408         delete this.ds;
47409     }
47410     if(this.cm){
47411         this.colModel = this.cm;
47412         delete this.cm;
47413     }
47414     if(this.sm){
47415         this.selModel = this.sm;
47416         delete this.sm;
47417     }
47418
47419     if (this.selModel) {
47420         this.selModel = Roo.factory(this.selModel, Roo.grid);
47421         this.sm = this.selModel;
47422         this.sm.xmodule = this.xmodule || false;
47423     }
47424     if (typeof(this.colModel.config) == 'undefined') {
47425         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47426         this.cm = this.colModel;
47427         this.cm.xmodule = this.xmodule || false;
47428     }
47429     if (this.dataSource) {
47430         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47431         this.ds = this.dataSource;
47432         this.ds.xmodule = this.xmodule || false;
47433          
47434     }
47435     
47436     
47437     
47438     if(this.width){
47439         this.container.setWidth(this.width);
47440     }
47441
47442     if(this.height){
47443         this.container.setHeight(this.height);
47444     }
47445     /** @private */
47446         this.addEvents({
47447         // raw events
47448         /**
47449          * @event click
47450          * The raw click event for the entire grid.
47451          * @param {Roo.EventObject} e
47452          */
47453         "click" : true,
47454         /**
47455          * @event dblclick
47456          * The raw dblclick event for the entire grid.
47457          * @param {Roo.EventObject} e
47458          */
47459         "dblclick" : true,
47460         /**
47461          * @event contextmenu
47462          * The raw contextmenu event for the entire grid.
47463          * @param {Roo.EventObject} e
47464          */
47465         "contextmenu" : true,
47466         /**
47467          * @event mousedown
47468          * The raw mousedown event for the entire grid.
47469          * @param {Roo.EventObject} e
47470          */
47471         "mousedown" : true,
47472         /**
47473          * @event mouseup
47474          * The raw mouseup event for the entire grid.
47475          * @param {Roo.EventObject} e
47476          */
47477         "mouseup" : true,
47478         /**
47479          * @event mouseover
47480          * The raw mouseover event for the entire grid.
47481          * @param {Roo.EventObject} e
47482          */
47483         "mouseover" : true,
47484         /**
47485          * @event mouseout
47486          * The raw mouseout event for the entire grid.
47487          * @param {Roo.EventObject} e
47488          */
47489         "mouseout" : true,
47490         /**
47491          * @event keypress
47492          * The raw keypress event for the entire grid.
47493          * @param {Roo.EventObject} e
47494          */
47495         "keypress" : true,
47496         /**
47497          * @event keydown
47498          * The raw keydown event for the entire grid.
47499          * @param {Roo.EventObject} e
47500          */
47501         "keydown" : true,
47502
47503         // custom events
47504
47505         /**
47506          * @event cellclick
47507          * Fires when a cell is clicked
47508          * @param {Grid} this
47509          * @param {Number} rowIndex
47510          * @param {Number} columnIndex
47511          * @param {Roo.EventObject} e
47512          */
47513         "cellclick" : true,
47514         /**
47515          * @event celldblclick
47516          * Fires when a cell is double clicked
47517          * @param {Grid} this
47518          * @param {Number} rowIndex
47519          * @param {Number} columnIndex
47520          * @param {Roo.EventObject} e
47521          */
47522         "celldblclick" : true,
47523         /**
47524          * @event rowclick
47525          * Fires when a row is clicked
47526          * @param {Grid} this
47527          * @param {Number} rowIndex
47528          * @param {Roo.EventObject} e
47529          */
47530         "rowclick" : true,
47531         /**
47532          * @event rowdblclick
47533          * Fires when a row is double clicked
47534          * @param {Grid} this
47535          * @param {Number} rowIndex
47536          * @param {Roo.EventObject} e
47537          */
47538         "rowdblclick" : true,
47539         /**
47540          * @event headerclick
47541          * Fires when a header is clicked
47542          * @param {Grid} this
47543          * @param {Number} columnIndex
47544          * @param {Roo.EventObject} e
47545          */
47546         "headerclick" : true,
47547         /**
47548          * @event headerdblclick
47549          * Fires when a header cell is double clicked
47550          * @param {Grid} this
47551          * @param {Number} columnIndex
47552          * @param {Roo.EventObject} e
47553          */
47554         "headerdblclick" : true,
47555         /**
47556          * @event rowcontextmenu
47557          * Fires when a row is right clicked
47558          * @param {Grid} this
47559          * @param {Number} rowIndex
47560          * @param {Roo.EventObject} e
47561          */
47562         "rowcontextmenu" : true,
47563         /**
47564          * @event cellcontextmenu
47565          * Fires when a cell is right clicked
47566          * @param {Grid} this
47567          * @param {Number} rowIndex
47568          * @param {Number} cellIndex
47569          * @param {Roo.EventObject} e
47570          */
47571          "cellcontextmenu" : true,
47572         /**
47573          * @event headercontextmenu
47574          * Fires when a header is right clicked
47575          * @param {Grid} this
47576          * @param {Number} columnIndex
47577          * @param {Roo.EventObject} e
47578          */
47579         "headercontextmenu" : true,
47580         /**
47581          * @event bodyscroll
47582          * Fires when the body element is scrolled
47583          * @param {Number} scrollLeft
47584          * @param {Number} scrollTop
47585          */
47586         "bodyscroll" : true,
47587         /**
47588          * @event columnresize
47589          * Fires when the user resizes a column
47590          * @param {Number} columnIndex
47591          * @param {Number} newSize
47592          */
47593         "columnresize" : true,
47594         /**
47595          * @event columnmove
47596          * Fires when the user moves a column
47597          * @param {Number} oldIndex
47598          * @param {Number} newIndex
47599          */
47600         "columnmove" : true,
47601         /**
47602          * @event startdrag
47603          * Fires when row(s) start being dragged
47604          * @param {Grid} this
47605          * @param {Roo.GridDD} dd The drag drop object
47606          * @param {event} e The raw browser event
47607          */
47608         "startdrag" : true,
47609         /**
47610          * @event enddrag
47611          * Fires when a drag operation is complete
47612          * @param {Grid} this
47613          * @param {Roo.GridDD} dd The drag drop object
47614          * @param {event} e The raw browser event
47615          */
47616         "enddrag" : true,
47617         /**
47618          * @event dragdrop
47619          * Fires when dragged row(s) are dropped on a valid DD target
47620          * @param {Grid} this
47621          * @param {Roo.GridDD} dd The drag drop object
47622          * @param {String} targetId The target drag drop object
47623          * @param {event} e The raw browser event
47624          */
47625         "dragdrop" : true,
47626         /**
47627          * @event dragover
47628          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47629          * @param {Grid} this
47630          * @param {Roo.GridDD} dd The drag drop object
47631          * @param {String} targetId The target drag drop object
47632          * @param {event} e The raw browser event
47633          */
47634         "dragover" : true,
47635         /**
47636          * @event dragenter
47637          *  Fires when the dragged row(s) first cross another DD target while being dragged
47638          * @param {Grid} this
47639          * @param {Roo.GridDD} dd The drag drop object
47640          * @param {String} targetId The target drag drop object
47641          * @param {event} e The raw browser event
47642          */
47643         "dragenter" : true,
47644         /**
47645          * @event dragout
47646          * Fires when the dragged row(s) leave another DD target while being dragged
47647          * @param {Grid} this
47648          * @param {Roo.GridDD} dd The drag drop object
47649          * @param {String} targetId The target drag drop object
47650          * @param {event} e The raw browser event
47651          */
47652         "dragout" : true,
47653         /**
47654          * @event rowclass
47655          * Fires when a row is rendered, so you can change add a style to it.
47656          * @param {GridView} gridview   The grid view
47657          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47658          */
47659         'rowclass' : true,
47660
47661         /**
47662          * @event render
47663          * Fires when the grid is rendered
47664          * @param {Grid} grid
47665          */
47666         'render' : true
47667     });
47668
47669     Roo.grid.Grid.superclass.constructor.call(this);
47670 };
47671 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47672     
47673     /**
47674      * @cfg {String} ddGroup - drag drop group.
47675      */
47676
47677     /**
47678      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47679      */
47680     minColumnWidth : 25,
47681
47682     /**
47683      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47684      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47685      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47686      */
47687     autoSizeColumns : false,
47688
47689     /**
47690      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47691      */
47692     autoSizeHeaders : true,
47693
47694     /**
47695      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47696      */
47697     monitorWindowResize : true,
47698
47699     /**
47700      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47701      * rows measured to get a columns size. Default is 0 (all rows).
47702      */
47703     maxRowsToMeasure : 0,
47704
47705     /**
47706      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47707      */
47708     trackMouseOver : true,
47709
47710     /**
47711     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47712     */
47713     
47714     /**
47715     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47716     */
47717     enableDragDrop : false,
47718     
47719     /**
47720     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47721     */
47722     enableColumnMove : true,
47723     
47724     /**
47725     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47726     */
47727     enableColumnHide : true,
47728     
47729     /**
47730     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47731     */
47732     enableRowHeightSync : false,
47733     
47734     /**
47735     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47736     */
47737     stripeRows : true,
47738     
47739     /**
47740     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47741     */
47742     autoHeight : false,
47743
47744     /**
47745      * @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.
47746      */
47747     autoExpandColumn : false,
47748
47749     /**
47750     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47751     * Default is 50.
47752     */
47753     autoExpandMin : 50,
47754
47755     /**
47756     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47757     */
47758     autoExpandMax : 1000,
47759
47760     /**
47761     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47762     */
47763     view : null,
47764
47765     /**
47766     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47767     */
47768     loadMask : false,
47769     /**
47770     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47771     */
47772     dropTarget: false,
47773     
47774    
47775     
47776     // private
47777     rendered : false,
47778
47779     /**
47780     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47781     * of a fixed width. Default is false.
47782     */
47783     /**
47784     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47785     */
47786     /**
47787      * Called once after all setup has been completed and the grid is ready to be rendered.
47788      * @return {Roo.grid.Grid} this
47789      */
47790     render : function()
47791     {
47792         var c = this.container;
47793         // try to detect autoHeight/width mode
47794         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47795             this.autoHeight = true;
47796         }
47797         var view = this.getView();
47798         view.init(this);
47799
47800         c.on("click", this.onClick, this);
47801         c.on("dblclick", this.onDblClick, this);
47802         c.on("contextmenu", this.onContextMenu, this);
47803         c.on("keydown", this.onKeyDown, this);
47804
47805         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47806
47807         this.getSelectionModel().init(this);
47808
47809         view.render();
47810
47811         if(this.loadMask){
47812             this.loadMask = new Roo.LoadMask(this.container,
47813                     Roo.apply({store:this.dataSource}, this.loadMask));
47814         }
47815         
47816         
47817         if (this.toolbar && this.toolbar.xtype) {
47818             this.toolbar.container = this.getView().getHeaderPanel(true);
47819             this.toolbar = new Roo.Toolbar(this.toolbar);
47820         }
47821         if (this.footer && this.footer.xtype) {
47822             this.footer.dataSource = this.getDataSource();
47823             this.footer.container = this.getView().getFooterPanel(true);
47824             this.footer = Roo.factory(this.footer, Roo);
47825         }
47826         if (this.dropTarget && this.dropTarget.xtype) {
47827             delete this.dropTarget.xtype;
47828             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47829         }
47830         
47831         
47832         this.rendered = true;
47833         this.fireEvent('render', this);
47834         return this;
47835     },
47836
47837         /**
47838          * Reconfigures the grid to use a different Store and Column Model.
47839          * The View will be bound to the new objects and refreshed.
47840          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47841          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47842          */
47843     reconfigure : function(dataSource, colModel){
47844         if(this.loadMask){
47845             this.loadMask.destroy();
47846             this.loadMask = new Roo.LoadMask(this.container,
47847                     Roo.apply({store:dataSource}, this.loadMask));
47848         }
47849         this.view.bind(dataSource, colModel);
47850         this.dataSource = dataSource;
47851         this.colModel = colModel;
47852         this.view.refresh(true);
47853     },
47854
47855     // private
47856     onKeyDown : function(e){
47857         this.fireEvent("keydown", e);
47858     },
47859
47860     /**
47861      * Destroy this grid.
47862      * @param {Boolean} removeEl True to remove the element
47863      */
47864     destroy : function(removeEl, keepListeners){
47865         if(this.loadMask){
47866             this.loadMask.destroy();
47867         }
47868         var c = this.container;
47869         c.removeAllListeners();
47870         this.view.destroy();
47871         this.colModel.purgeListeners();
47872         if(!keepListeners){
47873             this.purgeListeners();
47874         }
47875         c.update("");
47876         if(removeEl === true){
47877             c.remove();
47878         }
47879     },
47880
47881     // private
47882     processEvent : function(name, e){
47883         this.fireEvent(name, e);
47884         var t = e.getTarget();
47885         var v = this.view;
47886         var header = v.findHeaderIndex(t);
47887         if(header !== false){
47888             this.fireEvent("header" + name, this, header, e);
47889         }else{
47890             var row = v.findRowIndex(t);
47891             var cell = v.findCellIndex(t);
47892             if(row !== false){
47893                 this.fireEvent("row" + name, this, row, e);
47894                 if(cell !== false){
47895                     this.fireEvent("cell" + name, this, row, cell, e);
47896                 }
47897             }
47898         }
47899     },
47900
47901     // private
47902     onClick : function(e){
47903         this.processEvent("click", e);
47904     },
47905
47906     // private
47907     onContextMenu : function(e, t){
47908         this.processEvent("contextmenu", e);
47909     },
47910
47911     // private
47912     onDblClick : function(e){
47913         this.processEvent("dblclick", e);
47914     },
47915
47916     // private
47917     walkCells : function(row, col, step, fn, scope){
47918         var cm = this.colModel, clen = cm.getColumnCount();
47919         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47920         if(step < 0){
47921             if(col < 0){
47922                 row--;
47923                 first = false;
47924             }
47925             while(row >= 0){
47926                 if(!first){
47927                     col = clen-1;
47928                 }
47929                 first = false;
47930                 while(col >= 0){
47931                     if(fn.call(scope || this, row, col, cm) === true){
47932                         return [row, col];
47933                     }
47934                     col--;
47935                 }
47936                 row--;
47937             }
47938         } else {
47939             if(col >= clen){
47940                 row++;
47941                 first = false;
47942             }
47943             while(row < rlen){
47944                 if(!first){
47945                     col = 0;
47946                 }
47947                 first = false;
47948                 while(col < clen){
47949                     if(fn.call(scope || this, row, col, cm) === true){
47950                         return [row, col];
47951                     }
47952                     col++;
47953                 }
47954                 row++;
47955             }
47956         }
47957         return null;
47958     },
47959
47960     // private
47961     getSelections : function(){
47962         return this.selModel.getSelections();
47963     },
47964
47965     /**
47966      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47967      * but if manual update is required this method will initiate it.
47968      */
47969     autoSize : function(){
47970         if(this.rendered){
47971             this.view.layout();
47972             if(this.view.adjustForScroll){
47973                 this.view.adjustForScroll();
47974             }
47975         }
47976     },
47977
47978     /**
47979      * Returns the grid's underlying element.
47980      * @return {Element} The element
47981      */
47982     getGridEl : function(){
47983         return this.container;
47984     },
47985
47986     // private for compatibility, overridden by editor grid
47987     stopEditing : function(){},
47988
47989     /**
47990      * Returns the grid's SelectionModel.
47991      * @return {SelectionModel}
47992      */
47993     getSelectionModel : function(){
47994         if(!this.selModel){
47995             this.selModel = new Roo.grid.RowSelectionModel();
47996         }
47997         return this.selModel;
47998     },
47999
48000     /**
48001      * Returns the grid's DataSource.
48002      * @return {DataSource}
48003      */
48004     getDataSource : function(){
48005         return this.dataSource;
48006     },
48007
48008     /**
48009      * Returns the grid's ColumnModel.
48010      * @return {ColumnModel}
48011      */
48012     getColumnModel : function(){
48013         return this.colModel;
48014     },
48015
48016     /**
48017      * Returns the grid's GridView object.
48018      * @return {GridView}
48019      */
48020     getView : function(){
48021         if(!this.view){
48022             this.view = new Roo.grid.GridView(this.viewConfig);
48023         }
48024         return this.view;
48025     },
48026     /**
48027      * Called to get grid's drag proxy text, by default returns this.ddText.
48028      * @return {String}
48029      */
48030     getDragDropText : function(){
48031         var count = this.selModel.getCount();
48032         return String.format(this.ddText, count, count == 1 ? '' : 's');
48033     }
48034 });
48035 /**
48036  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48037  * %0 is replaced with the number of selected rows.
48038  * @type String
48039  */
48040 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48041  * Based on:
48042  * Ext JS Library 1.1.1
48043  * Copyright(c) 2006-2007, Ext JS, LLC.
48044  *
48045  * Originally Released Under LGPL - original licence link has changed is not relivant.
48046  *
48047  * Fork - LGPL
48048  * <script type="text/javascript">
48049  */
48050  
48051 Roo.grid.AbstractGridView = function(){
48052         this.grid = null;
48053         
48054         this.events = {
48055             "beforerowremoved" : true,
48056             "beforerowsinserted" : true,
48057             "beforerefresh" : true,
48058             "rowremoved" : true,
48059             "rowsinserted" : true,
48060             "rowupdated" : true,
48061             "refresh" : true
48062         };
48063     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48064 };
48065
48066 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48067     rowClass : "x-grid-row",
48068     cellClass : "x-grid-cell",
48069     tdClass : "x-grid-td",
48070     hdClass : "x-grid-hd",
48071     splitClass : "x-grid-hd-split",
48072     
48073         init: function(grid){
48074         this.grid = grid;
48075                 var cid = this.grid.getGridEl().id;
48076         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48077         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48078         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48079         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48080         },
48081         
48082         getColumnRenderers : function(){
48083         var renderers = [];
48084         var cm = this.grid.colModel;
48085         var colCount = cm.getColumnCount();
48086         for(var i = 0; i < colCount; i++){
48087             renderers[i] = cm.getRenderer(i);
48088         }
48089         return renderers;
48090     },
48091     
48092     getColumnIds : function(){
48093         var ids = [];
48094         var cm = this.grid.colModel;
48095         var colCount = cm.getColumnCount();
48096         for(var i = 0; i < colCount; i++){
48097             ids[i] = cm.getColumnId(i);
48098         }
48099         return ids;
48100     },
48101     
48102     getDataIndexes : function(){
48103         if(!this.indexMap){
48104             this.indexMap = this.buildIndexMap();
48105         }
48106         return this.indexMap.colToData;
48107     },
48108     
48109     getColumnIndexByDataIndex : function(dataIndex){
48110         if(!this.indexMap){
48111             this.indexMap = this.buildIndexMap();
48112         }
48113         return this.indexMap.dataToCol[dataIndex];
48114     },
48115     
48116     /**
48117      * Set a css style for a column dynamically. 
48118      * @param {Number} colIndex The index of the column
48119      * @param {String} name The css property name
48120      * @param {String} value The css value
48121      */
48122     setCSSStyle : function(colIndex, name, value){
48123         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48124         Roo.util.CSS.updateRule(selector, name, value);
48125     },
48126     
48127     generateRules : function(cm){
48128         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48129         Roo.util.CSS.removeStyleSheet(rulesId);
48130         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48131             var cid = cm.getColumnId(i);
48132             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48133                          this.tdSelector, cid, " {\n}\n",
48134                          this.hdSelector, cid, " {\n}\n",
48135                          this.splitSelector, cid, " {\n}\n");
48136         }
48137         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48138     }
48139 });/*
48140  * Based on:
48141  * Ext JS Library 1.1.1
48142  * Copyright(c) 2006-2007, Ext JS, LLC.
48143  *
48144  * Originally Released Under LGPL - original licence link has changed is not relivant.
48145  *
48146  * Fork - LGPL
48147  * <script type="text/javascript">
48148  */
48149
48150 // private
48151 // This is a support class used internally by the Grid components
48152 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48153     this.grid = grid;
48154     this.view = grid.getView();
48155     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48156     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48157     if(hd2){
48158         this.setHandleElId(Roo.id(hd));
48159         this.setOuterHandleElId(Roo.id(hd2));
48160     }
48161     this.scroll = false;
48162 };
48163 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48164     maxDragWidth: 120,
48165     getDragData : function(e){
48166         var t = Roo.lib.Event.getTarget(e);
48167         var h = this.view.findHeaderCell(t);
48168         if(h){
48169             return {ddel: h.firstChild, header:h};
48170         }
48171         return false;
48172     },
48173
48174     onInitDrag : function(e){
48175         this.view.headersDisabled = true;
48176         var clone = this.dragData.ddel.cloneNode(true);
48177         clone.id = Roo.id();
48178         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48179         this.proxy.update(clone);
48180         return true;
48181     },
48182
48183     afterValidDrop : function(){
48184         var v = this.view;
48185         setTimeout(function(){
48186             v.headersDisabled = false;
48187         }, 50);
48188     },
48189
48190     afterInvalidDrop : function(){
48191         var v = this.view;
48192         setTimeout(function(){
48193             v.headersDisabled = false;
48194         }, 50);
48195     }
48196 });
48197 /*
48198  * Based on:
48199  * Ext JS Library 1.1.1
48200  * Copyright(c) 2006-2007, Ext JS, LLC.
48201  *
48202  * Originally Released Under LGPL - original licence link has changed is not relivant.
48203  *
48204  * Fork - LGPL
48205  * <script type="text/javascript">
48206  */
48207 // private
48208 // This is a support class used internally by the Grid components
48209 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48210     this.grid = grid;
48211     this.view = grid.getView();
48212     // split the proxies so they don't interfere with mouse events
48213     this.proxyTop = Roo.DomHelper.append(document.body, {
48214         cls:"col-move-top", html:"&#160;"
48215     }, true);
48216     this.proxyBottom = Roo.DomHelper.append(document.body, {
48217         cls:"col-move-bottom", html:"&#160;"
48218     }, true);
48219     this.proxyTop.hide = this.proxyBottom.hide = function(){
48220         this.setLeftTop(-100,-100);
48221         this.setStyle("visibility", "hidden");
48222     };
48223     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48224     // temporarily disabled
48225     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48226     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48227 };
48228 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48229     proxyOffsets : [-4, -9],
48230     fly: Roo.Element.fly,
48231
48232     getTargetFromEvent : function(e){
48233         var t = Roo.lib.Event.getTarget(e);
48234         var cindex = this.view.findCellIndex(t);
48235         if(cindex !== false){
48236             return this.view.getHeaderCell(cindex);
48237         }
48238         return null;
48239     },
48240
48241     nextVisible : function(h){
48242         var v = this.view, cm = this.grid.colModel;
48243         h = h.nextSibling;
48244         while(h){
48245             if(!cm.isHidden(v.getCellIndex(h))){
48246                 return h;
48247             }
48248             h = h.nextSibling;
48249         }
48250         return null;
48251     },
48252
48253     prevVisible : function(h){
48254         var v = this.view, cm = this.grid.colModel;
48255         h = h.prevSibling;
48256         while(h){
48257             if(!cm.isHidden(v.getCellIndex(h))){
48258                 return h;
48259             }
48260             h = h.prevSibling;
48261         }
48262         return null;
48263     },
48264
48265     positionIndicator : function(h, n, e){
48266         var x = Roo.lib.Event.getPageX(e);
48267         var r = Roo.lib.Dom.getRegion(n.firstChild);
48268         var px, pt, py = r.top + this.proxyOffsets[1];
48269         if((r.right - x) <= (r.right-r.left)/2){
48270             px = r.right+this.view.borderWidth;
48271             pt = "after";
48272         }else{
48273             px = r.left;
48274             pt = "before";
48275         }
48276         var oldIndex = this.view.getCellIndex(h);
48277         var newIndex = this.view.getCellIndex(n);
48278
48279         if(this.grid.colModel.isFixed(newIndex)){
48280             return false;
48281         }
48282
48283         var locked = this.grid.colModel.isLocked(newIndex);
48284
48285         if(pt == "after"){
48286             newIndex++;
48287         }
48288         if(oldIndex < newIndex){
48289             newIndex--;
48290         }
48291         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48292             return false;
48293         }
48294         px +=  this.proxyOffsets[0];
48295         this.proxyTop.setLeftTop(px, py);
48296         this.proxyTop.show();
48297         if(!this.bottomOffset){
48298             this.bottomOffset = this.view.mainHd.getHeight();
48299         }
48300         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48301         this.proxyBottom.show();
48302         return pt;
48303     },
48304
48305     onNodeEnter : function(n, dd, e, data){
48306         if(data.header != n){
48307             this.positionIndicator(data.header, n, e);
48308         }
48309     },
48310
48311     onNodeOver : function(n, dd, e, data){
48312         var result = false;
48313         if(data.header != n){
48314             result = this.positionIndicator(data.header, n, e);
48315         }
48316         if(!result){
48317             this.proxyTop.hide();
48318             this.proxyBottom.hide();
48319         }
48320         return result ? this.dropAllowed : this.dropNotAllowed;
48321     },
48322
48323     onNodeOut : function(n, dd, e, data){
48324         this.proxyTop.hide();
48325         this.proxyBottom.hide();
48326     },
48327
48328     onNodeDrop : function(n, dd, e, data){
48329         var h = data.header;
48330         if(h != n){
48331             var cm = this.grid.colModel;
48332             var x = Roo.lib.Event.getPageX(e);
48333             var r = Roo.lib.Dom.getRegion(n.firstChild);
48334             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48335             var oldIndex = this.view.getCellIndex(h);
48336             var newIndex = this.view.getCellIndex(n);
48337             var locked = cm.isLocked(newIndex);
48338             if(pt == "after"){
48339                 newIndex++;
48340             }
48341             if(oldIndex < newIndex){
48342                 newIndex--;
48343             }
48344             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48345                 return false;
48346             }
48347             cm.setLocked(oldIndex, locked, true);
48348             cm.moveColumn(oldIndex, newIndex);
48349             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48350             return true;
48351         }
48352         return false;
48353     }
48354 });
48355 /*
48356  * Based on:
48357  * Ext JS Library 1.1.1
48358  * Copyright(c) 2006-2007, Ext JS, LLC.
48359  *
48360  * Originally Released Under LGPL - original licence link has changed is not relivant.
48361  *
48362  * Fork - LGPL
48363  * <script type="text/javascript">
48364  */
48365   
48366 /**
48367  * @class Roo.grid.GridView
48368  * @extends Roo.util.Observable
48369  *
48370  * @constructor
48371  * @param {Object} config
48372  */
48373 Roo.grid.GridView = function(config){
48374     Roo.grid.GridView.superclass.constructor.call(this);
48375     this.el = null;
48376
48377     Roo.apply(this, config);
48378 };
48379
48380 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48381
48382     /**
48383      * Override this function to apply custom css classes to rows during rendering
48384      * @param {Record} record The record
48385      * @param {Number} index
48386      * @method getRowClass
48387      */
48388     rowClass : "x-grid-row",
48389
48390     cellClass : "x-grid-col",
48391
48392     tdClass : "x-grid-td",
48393
48394     hdClass : "x-grid-hd",
48395
48396     splitClass : "x-grid-split",
48397
48398     sortClasses : ["sort-asc", "sort-desc"],
48399
48400     enableMoveAnim : false,
48401
48402     hlColor: "C3DAF9",
48403
48404     dh : Roo.DomHelper,
48405
48406     fly : Roo.Element.fly,
48407
48408     css : Roo.util.CSS,
48409
48410     borderWidth: 1,
48411
48412     splitOffset: 3,
48413
48414     scrollIncrement : 22,
48415
48416     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48417
48418     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48419
48420     bind : function(ds, cm){
48421         if(this.ds){
48422             this.ds.un("load", this.onLoad, this);
48423             this.ds.un("datachanged", this.onDataChange, this);
48424             this.ds.un("add", this.onAdd, this);
48425             this.ds.un("remove", this.onRemove, this);
48426             this.ds.un("update", this.onUpdate, this);
48427             this.ds.un("clear", this.onClear, this);
48428         }
48429         if(ds){
48430             ds.on("load", this.onLoad, this);
48431             ds.on("datachanged", this.onDataChange, this);
48432             ds.on("add", this.onAdd, this);
48433             ds.on("remove", this.onRemove, this);
48434             ds.on("update", this.onUpdate, this);
48435             ds.on("clear", this.onClear, this);
48436         }
48437         this.ds = ds;
48438
48439         if(this.cm){
48440             this.cm.un("widthchange", this.onColWidthChange, this);
48441             this.cm.un("headerchange", this.onHeaderChange, this);
48442             this.cm.un("hiddenchange", this.onHiddenChange, this);
48443             this.cm.un("columnmoved", this.onColumnMove, this);
48444             this.cm.un("columnlockchange", this.onColumnLock, this);
48445         }
48446         if(cm){
48447             this.generateRules(cm);
48448             cm.on("widthchange", this.onColWidthChange, this);
48449             cm.on("headerchange", this.onHeaderChange, this);
48450             cm.on("hiddenchange", this.onHiddenChange, this);
48451             cm.on("columnmoved", this.onColumnMove, this);
48452             cm.on("columnlockchange", this.onColumnLock, this);
48453         }
48454         this.cm = cm;
48455     },
48456
48457     init: function(grid){
48458         Roo.grid.GridView.superclass.init.call(this, grid);
48459
48460         this.bind(grid.dataSource, grid.colModel);
48461
48462         grid.on("headerclick", this.handleHeaderClick, this);
48463
48464         if(grid.trackMouseOver){
48465             grid.on("mouseover", this.onRowOver, this);
48466             grid.on("mouseout", this.onRowOut, this);
48467         }
48468         grid.cancelTextSelection = function(){};
48469         this.gridId = grid.id;
48470
48471         var tpls = this.templates || {};
48472
48473         if(!tpls.master){
48474             tpls.master = new Roo.Template(
48475                '<div class="x-grid" hidefocus="true">',
48476                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48477                   '<div class="x-grid-topbar"></div>',
48478                   '<div class="x-grid-scroller"><div></div></div>',
48479                   '<div class="x-grid-locked">',
48480                       '<div class="x-grid-header">{lockedHeader}</div>',
48481                       '<div class="x-grid-body">{lockedBody}</div>',
48482                   "</div>",
48483                   '<div class="x-grid-viewport">',
48484                       '<div class="x-grid-header">{header}</div>',
48485                       '<div class="x-grid-body">{body}</div>',
48486                   "</div>",
48487                   '<div class="x-grid-bottombar"></div>',
48488                  
48489                   '<div class="x-grid-resize-proxy">&#160;</div>',
48490                "</div>"
48491             );
48492             tpls.master.disableformats = true;
48493         }
48494
48495         if(!tpls.header){
48496             tpls.header = new Roo.Template(
48497                '<table border="0" cellspacing="0" cellpadding="0">',
48498                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48499                "</table>{splits}"
48500             );
48501             tpls.header.disableformats = true;
48502         }
48503         tpls.header.compile();
48504
48505         if(!tpls.hcell){
48506             tpls.hcell = new Roo.Template(
48507                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48508                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48509                 "</div></td>"
48510              );
48511              tpls.hcell.disableFormats = true;
48512         }
48513         tpls.hcell.compile();
48514
48515         if(!tpls.hsplit){
48516             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48517             tpls.hsplit.disableFormats = true;
48518         }
48519         tpls.hsplit.compile();
48520
48521         if(!tpls.body){
48522             tpls.body = new Roo.Template(
48523                '<table border="0" cellspacing="0" cellpadding="0">',
48524                "<tbody>{rows}</tbody>",
48525                "</table>"
48526             );
48527             tpls.body.disableFormats = true;
48528         }
48529         tpls.body.compile();
48530
48531         if(!tpls.row){
48532             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48533             tpls.row.disableFormats = true;
48534         }
48535         tpls.row.compile();
48536
48537         if(!tpls.cell){
48538             tpls.cell = new Roo.Template(
48539                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48540                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48541                 "</td>"
48542             );
48543             tpls.cell.disableFormats = true;
48544         }
48545         tpls.cell.compile();
48546
48547         this.templates = tpls;
48548     },
48549
48550     // remap these for backwards compat
48551     onColWidthChange : function(){
48552         this.updateColumns.apply(this, arguments);
48553     },
48554     onHeaderChange : function(){
48555         this.updateHeaders.apply(this, arguments);
48556     }, 
48557     onHiddenChange : function(){
48558         this.handleHiddenChange.apply(this, arguments);
48559     },
48560     onColumnMove : function(){
48561         this.handleColumnMove.apply(this, arguments);
48562     },
48563     onColumnLock : function(){
48564         this.handleLockChange.apply(this, arguments);
48565     },
48566
48567     onDataChange : function(){
48568         this.refresh();
48569         this.updateHeaderSortState();
48570     },
48571
48572     onClear : function(){
48573         this.refresh();
48574     },
48575
48576     onUpdate : function(ds, record){
48577         this.refreshRow(record);
48578     },
48579
48580     refreshRow : function(record){
48581         var ds = this.ds, index;
48582         if(typeof record == 'number'){
48583             index = record;
48584             record = ds.getAt(index);
48585         }else{
48586             index = ds.indexOf(record);
48587         }
48588         this.insertRows(ds, index, index, true);
48589         this.onRemove(ds, record, index+1, true);
48590         this.syncRowHeights(index, index);
48591         this.layout();
48592         this.fireEvent("rowupdated", this, index, record);
48593     },
48594
48595     onAdd : function(ds, records, index){
48596         this.insertRows(ds, index, index + (records.length-1));
48597     },
48598
48599     onRemove : function(ds, record, index, isUpdate){
48600         if(isUpdate !== true){
48601             this.fireEvent("beforerowremoved", this, index, record);
48602         }
48603         var bt = this.getBodyTable(), lt = this.getLockedTable();
48604         if(bt.rows[index]){
48605             bt.firstChild.removeChild(bt.rows[index]);
48606         }
48607         if(lt.rows[index]){
48608             lt.firstChild.removeChild(lt.rows[index]);
48609         }
48610         if(isUpdate !== true){
48611             this.stripeRows(index);
48612             this.syncRowHeights(index, index);
48613             this.layout();
48614             this.fireEvent("rowremoved", this, index, record);
48615         }
48616     },
48617
48618     onLoad : function(){
48619         this.scrollToTop();
48620     },
48621
48622     /**
48623      * Scrolls the grid to the top
48624      */
48625     scrollToTop : function(){
48626         if(this.scroller){
48627             this.scroller.dom.scrollTop = 0;
48628             this.syncScroll();
48629         }
48630     },
48631
48632     /**
48633      * Gets a panel in the header of the grid that can be used for toolbars etc.
48634      * After modifying the contents of this panel a call to grid.autoSize() may be
48635      * required to register any changes in size.
48636      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48637      * @return Roo.Element
48638      */
48639     getHeaderPanel : function(doShow){
48640         if(doShow){
48641             this.headerPanel.show();
48642         }
48643         return this.headerPanel;
48644     },
48645
48646     /**
48647      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48648      * After modifying the contents of this panel a call to grid.autoSize() may be
48649      * required to register any changes in size.
48650      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48651      * @return Roo.Element
48652      */
48653     getFooterPanel : function(doShow){
48654         if(doShow){
48655             this.footerPanel.show();
48656         }
48657         return this.footerPanel;
48658     },
48659
48660     initElements : function(){
48661         var E = Roo.Element;
48662         var el = this.grid.getGridEl().dom.firstChild;
48663         var cs = el.childNodes;
48664
48665         this.el = new E(el);
48666         
48667          this.focusEl = new E(el.firstChild);
48668         this.focusEl.swallowEvent("click", true);
48669         
48670         this.headerPanel = new E(cs[1]);
48671         this.headerPanel.enableDisplayMode("block");
48672
48673         this.scroller = new E(cs[2]);
48674         this.scrollSizer = new E(this.scroller.dom.firstChild);
48675
48676         this.lockedWrap = new E(cs[3]);
48677         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48678         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48679
48680         this.mainWrap = new E(cs[4]);
48681         this.mainHd = new E(this.mainWrap.dom.firstChild);
48682         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48683
48684         this.footerPanel = new E(cs[5]);
48685         this.footerPanel.enableDisplayMode("block");
48686
48687         this.resizeProxy = new E(cs[6]);
48688
48689         this.headerSelector = String.format(
48690            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48691            this.lockedHd.id, this.mainHd.id
48692         );
48693
48694         this.splitterSelector = String.format(
48695            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48696            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48697         );
48698     },
48699     idToCssName : function(s)
48700     {
48701         return s.replace(/[^a-z0-9]+/ig, '-');
48702     },
48703
48704     getHeaderCell : function(index){
48705         return Roo.DomQuery.select(this.headerSelector)[index];
48706     },
48707
48708     getHeaderCellMeasure : function(index){
48709         return this.getHeaderCell(index).firstChild;
48710     },
48711
48712     getHeaderCellText : function(index){
48713         return this.getHeaderCell(index).firstChild.firstChild;
48714     },
48715
48716     getLockedTable : function(){
48717         return this.lockedBody.dom.firstChild;
48718     },
48719
48720     getBodyTable : function(){
48721         return this.mainBody.dom.firstChild;
48722     },
48723
48724     getLockedRow : function(index){
48725         return this.getLockedTable().rows[index];
48726     },
48727
48728     getRow : function(index){
48729         return this.getBodyTable().rows[index];
48730     },
48731
48732     getRowComposite : function(index){
48733         if(!this.rowEl){
48734             this.rowEl = new Roo.CompositeElementLite();
48735         }
48736         var els = [], lrow, mrow;
48737         if(lrow = this.getLockedRow(index)){
48738             els.push(lrow);
48739         }
48740         if(mrow = this.getRow(index)){
48741             els.push(mrow);
48742         }
48743         this.rowEl.elements = els;
48744         return this.rowEl;
48745     },
48746     /**
48747      * Gets the 'td' of the cell
48748      * 
48749      * @param {Integer} rowIndex row to select
48750      * @param {Integer} colIndex column to select
48751      * 
48752      * @return {Object} 
48753      */
48754     getCell : function(rowIndex, colIndex){
48755         var locked = this.cm.getLockedCount();
48756         var source;
48757         if(colIndex < locked){
48758             source = this.lockedBody.dom.firstChild;
48759         }else{
48760             source = this.mainBody.dom.firstChild;
48761             colIndex -= locked;
48762         }
48763         return source.rows[rowIndex].childNodes[colIndex];
48764     },
48765
48766     getCellText : function(rowIndex, colIndex){
48767         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48768     },
48769
48770     getCellBox : function(cell){
48771         var b = this.fly(cell).getBox();
48772         if(Roo.isOpera){ // opera fails to report the Y
48773             b.y = cell.offsetTop + this.mainBody.getY();
48774         }
48775         return b;
48776     },
48777
48778     getCellIndex : function(cell){
48779         var id = String(cell.className).match(this.cellRE);
48780         if(id){
48781             return parseInt(id[1], 10);
48782         }
48783         return 0;
48784     },
48785
48786     findHeaderIndex : function(n){
48787         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48788         return r ? this.getCellIndex(r) : false;
48789     },
48790
48791     findHeaderCell : function(n){
48792         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48793         return r ? r : false;
48794     },
48795
48796     findRowIndex : function(n){
48797         if(!n){
48798             return false;
48799         }
48800         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48801         return r ? r.rowIndex : false;
48802     },
48803
48804     findCellIndex : function(node){
48805         var stop = this.el.dom;
48806         while(node && node != stop){
48807             if(this.findRE.test(node.className)){
48808                 return this.getCellIndex(node);
48809             }
48810             node = node.parentNode;
48811         }
48812         return false;
48813     },
48814
48815     getColumnId : function(index){
48816         return this.cm.getColumnId(index);
48817     },
48818
48819     getSplitters : function()
48820     {
48821         if(this.splitterSelector){
48822            return Roo.DomQuery.select(this.splitterSelector);
48823         }else{
48824             return null;
48825       }
48826     },
48827
48828     getSplitter : function(index){
48829         return this.getSplitters()[index];
48830     },
48831
48832     onRowOver : function(e, t){
48833         var row;
48834         if((row = this.findRowIndex(t)) !== false){
48835             this.getRowComposite(row).addClass("x-grid-row-over");
48836         }
48837     },
48838
48839     onRowOut : function(e, t){
48840         var row;
48841         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48842             this.getRowComposite(row).removeClass("x-grid-row-over");
48843         }
48844     },
48845
48846     renderHeaders : function(){
48847         var cm = this.cm;
48848         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48849         var cb = [], lb = [], sb = [], lsb = [], p = {};
48850         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48851             p.cellId = "x-grid-hd-0-" + i;
48852             p.splitId = "x-grid-csplit-0-" + i;
48853             p.id = cm.getColumnId(i);
48854             p.title = cm.getColumnTooltip(i) || "";
48855             p.value = cm.getColumnHeader(i) || "";
48856             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48857             if(!cm.isLocked(i)){
48858                 cb[cb.length] = ct.apply(p);
48859                 sb[sb.length] = st.apply(p);
48860             }else{
48861                 lb[lb.length] = ct.apply(p);
48862                 lsb[lsb.length] = st.apply(p);
48863             }
48864         }
48865         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48866                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48867     },
48868
48869     updateHeaders : function(){
48870         var html = this.renderHeaders();
48871         this.lockedHd.update(html[0]);
48872         this.mainHd.update(html[1]);
48873     },
48874
48875     /**
48876      * Focuses the specified row.
48877      * @param {Number} row The row index
48878      */
48879     focusRow : function(row)
48880     {
48881         //Roo.log('GridView.focusRow');
48882         var x = this.scroller.dom.scrollLeft;
48883         this.focusCell(row, 0, false);
48884         this.scroller.dom.scrollLeft = x;
48885     },
48886
48887     /**
48888      * Focuses the specified cell.
48889      * @param {Number} row The row index
48890      * @param {Number} col The column index
48891      * @param {Boolean} hscroll false to disable horizontal scrolling
48892      */
48893     focusCell : function(row, col, hscroll)
48894     {
48895         //Roo.log('GridView.focusCell');
48896         var el = this.ensureVisible(row, col, hscroll);
48897         this.focusEl.alignTo(el, "tl-tl");
48898         if(Roo.isGecko){
48899             this.focusEl.focus();
48900         }else{
48901             this.focusEl.focus.defer(1, this.focusEl);
48902         }
48903     },
48904
48905     /**
48906      * Scrolls the specified cell into view
48907      * @param {Number} row The row index
48908      * @param {Number} col The column index
48909      * @param {Boolean} hscroll false to disable horizontal scrolling
48910      */
48911     ensureVisible : function(row, col, hscroll)
48912     {
48913         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48914         //return null; //disable for testing.
48915         if(typeof row != "number"){
48916             row = row.rowIndex;
48917         }
48918         if(row < 0 && row >= this.ds.getCount()){
48919             return  null;
48920         }
48921         col = (col !== undefined ? col : 0);
48922         var cm = this.grid.colModel;
48923         while(cm.isHidden(col)){
48924             col++;
48925         }
48926
48927         var el = this.getCell(row, col);
48928         if(!el){
48929             return null;
48930         }
48931         var c = this.scroller.dom;
48932
48933         var ctop = parseInt(el.offsetTop, 10);
48934         var cleft = parseInt(el.offsetLeft, 10);
48935         var cbot = ctop + el.offsetHeight;
48936         var cright = cleft + el.offsetWidth;
48937         
48938         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48939         var stop = parseInt(c.scrollTop, 10);
48940         var sleft = parseInt(c.scrollLeft, 10);
48941         var sbot = stop + ch;
48942         var sright = sleft + c.clientWidth;
48943         /*
48944         Roo.log('GridView.ensureVisible:' +
48945                 ' ctop:' + ctop +
48946                 ' c.clientHeight:' + c.clientHeight +
48947                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48948                 ' stop:' + stop +
48949                 ' cbot:' + cbot +
48950                 ' sbot:' + sbot +
48951                 ' ch:' + ch  
48952                 );
48953         */
48954         if(ctop < stop){
48955              c.scrollTop = ctop;
48956             //Roo.log("set scrolltop to ctop DISABLE?");
48957         }else if(cbot > sbot){
48958             //Roo.log("set scrolltop to cbot-ch");
48959             c.scrollTop = cbot-ch;
48960         }
48961         
48962         if(hscroll !== false){
48963             if(cleft < sleft){
48964                 c.scrollLeft = cleft;
48965             }else if(cright > sright){
48966                 c.scrollLeft = cright-c.clientWidth;
48967             }
48968         }
48969          
48970         return el;
48971     },
48972
48973     updateColumns : function(){
48974         this.grid.stopEditing();
48975         var cm = this.grid.colModel, colIds = this.getColumnIds();
48976         //var totalWidth = cm.getTotalWidth();
48977         var pos = 0;
48978         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48979             //if(cm.isHidden(i)) continue;
48980             var w = cm.getColumnWidth(i);
48981             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48982             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48983         }
48984         this.updateSplitters();
48985     },
48986
48987     generateRules : function(cm){
48988         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48989         Roo.util.CSS.removeStyleSheet(rulesId);
48990         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48991             var cid = cm.getColumnId(i);
48992             var align = '';
48993             if(cm.config[i].align){
48994                 align = 'text-align:'+cm.config[i].align+';';
48995             }
48996             var hidden = '';
48997             if(cm.isHidden(i)){
48998                 hidden = 'display:none;';
48999             }
49000             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49001             ruleBuf.push(
49002                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49003                     this.hdSelector, cid, " {\n", align, width, "}\n",
49004                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49005                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49006         }
49007         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49008     },
49009
49010     updateSplitters : function(){
49011         var cm = this.cm, s = this.getSplitters();
49012         if(s){ // splitters not created yet
49013             var pos = 0, locked = true;
49014             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49015                 if(cm.isHidden(i)) continue;
49016                 var w = cm.getColumnWidth(i); // make sure it's a number
49017                 if(!cm.isLocked(i) && locked){
49018                     pos = 0;
49019                     locked = false;
49020                 }
49021                 pos += w;
49022                 s[i].style.left = (pos-this.splitOffset) + "px";
49023             }
49024         }
49025     },
49026
49027     handleHiddenChange : function(colModel, colIndex, hidden){
49028         if(hidden){
49029             this.hideColumn(colIndex);
49030         }else{
49031             this.unhideColumn(colIndex);
49032         }
49033     },
49034
49035     hideColumn : function(colIndex){
49036         var cid = this.getColumnId(colIndex);
49037         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49038         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49039         if(Roo.isSafari){
49040             this.updateHeaders();
49041         }
49042         this.updateSplitters();
49043         this.layout();
49044     },
49045
49046     unhideColumn : function(colIndex){
49047         var cid = this.getColumnId(colIndex);
49048         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49049         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49050
49051         if(Roo.isSafari){
49052             this.updateHeaders();
49053         }
49054         this.updateSplitters();
49055         this.layout();
49056     },
49057
49058     insertRows : function(dm, firstRow, lastRow, isUpdate){
49059         if(firstRow == 0 && lastRow == dm.getCount()-1){
49060             this.refresh();
49061         }else{
49062             if(!isUpdate){
49063                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49064             }
49065             var s = this.getScrollState();
49066             var markup = this.renderRows(firstRow, lastRow);
49067             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49068             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49069             this.restoreScroll(s);
49070             if(!isUpdate){
49071                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49072                 this.syncRowHeights(firstRow, lastRow);
49073                 this.stripeRows(firstRow);
49074                 this.layout();
49075             }
49076         }
49077     },
49078
49079     bufferRows : function(markup, target, index){
49080         var before = null, trows = target.rows, tbody = target.tBodies[0];
49081         if(index < trows.length){
49082             before = trows[index];
49083         }
49084         var b = document.createElement("div");
49085         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49086         var rows = b.firstChild.rows;
49087         for(var i = 0, len = rows.length; i < len; i++){
49088             if(before){
49089                 tbody.insertBefore(rows[0], before);
49090             }else{
49091                 tbody.appendChild(rows[0]);
49092             }
49093         }
49094         b.innerHTML = "";
49095         b = null;
49096     },
49097
49098     deleteRows : function(dm, firstRow, lastRow){
49099         if(dm.getRowCount()<1){
49100             this.fireEvent("beforerefresh", this);
49101             this.mainBody.update("");
49102             this.lockedBody.update("");
49103             this.fireEvent("refresh", this);
49104         }else{
49105             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49106             var bt = this.getBodyTable();
49107             var tbody = bt.firstChild;
49108             var rows = bt.rows;
49109             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49110                 tbody.removeChild(rows[firstRow]);
49111             }
49112             this.stripeRows(firstRow);
49113             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49114         }
49115     },
49116
49117     updateRows : function(dataSource, firstRow, lastRow){
49118         var s = this.getScrollState();
49119         this.refresh();
49120         this.restoreScroll(s);
49121     },
49122
49123     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49124         if(!noRefresh){
49125            this.refresh();
49126         }
49127         this.updateHeaderSortState();
49128     },
49129
49130     getScrollState : function(){
49131         
49132         var sb = this.scroller.dom;
49133         return {left: sb.scrollLeft, top: sb.scrollTop};
49134     },
49135
49136     stripeRows : function(startRow){
49137         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49138             return;
49139         }
49140         startRow = startRow || 0;
49141         var rows = this.getBodyTable().rows;
49142         var lrows = this.getLockedTable().rows;
49143         var cls = ' x-grid-row-alt ';
49144         for(var i = startRow, len = rows.length; i < len; i++){
49145             var row = rows[i], lrow = lrows[i];
49146             var isAlt = ((i+1) % 2 == 0);
49147             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49148             if(isAlt == hasAlt){
49149                 continue;
49150             }
49151             if(isAlt){
49152                 row.className += " x-grid-row-alt";
49153             }else{
49154                 row.className = row.className.replace("x-grid-row-alt", "");
49155             }
49156             if(lrow){
49157                 lrow.className = row.className;
49158             }
49159         }
49160     },
49161
49162     restoreScroll : function(state){
49163         //Roo.log('GridView.restoreScroll');
49164         var sb = this.scroller.dom;
49165         sb.scrollLeft = state.left;
49166         sb.scrollTop = state.top;
49167         this.syncScroll();
49168     },
49169
49170     syncScroll : function(){
49171         //Roo.log('GridView.syncScroll');
49172         var sb = this.scroller.dom;
49173         var sh = this.mainHd.dom;
49174         var bs = this.mainBody.dom;
49175         var lv = this.lockedBody.dom;
49176         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49177         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49178     },
49179
49180     handleScroll : function(e){
49181         this.syncScroll();
49182         var sb = this.scroller.dom;
49183         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49184         e.stopEvent();
49185     },
49186
49187     handleWheel : function(e){
49188         var d = e.getWheelDelta();
49189         this.scroller.dom.scrollTop -= d*22;
49190         // set this here to prevent jumpy scrolling on large tables
49191         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49192         e.stopEvent();
49193     },
49194
49195     renderRows : function(startRow, endRow){
49196         // pull in all the crap needed to render rows
49197         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49198         var colCount = cm.getColumnCount();
49199
49200         if(ds.getCount() < 1){
49201             return ["", ""];
49202         }
49203
49204         // build a map for all the columns
49205         var cs = [];
49206         for(var i = 0; i < colCount; i++){
49207             var name = cm.getDataIndex(i);
49208             cs[i] = {
49209                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49210                 renderer : cm.getRenderer(i),
49211                 id : cm.getColumnId(i),
49212                 locked : cm.isLocked(i)
49213             };
49214         }
49215
49216         startRow = startRow || 0;
49217         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49218
49219         // records to render
49220         var rs = ds.getRange(startRow, endRow);
49221
49222         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49223     },
49224
49225     // As much as I hate to duplicate code, this was branched because FireFox really hates
49226     // [].join("") on strings. The performance difference was substantial enough to
49227     // branch this function
49228     doRender : Roo.isGecko ?
49229             function(cs, rs, ds, startRow, colCount, stripe){
49230                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49231                 // buffers
49232                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49233                 
49234                 var hasListener = this.grid.hasListener('rowclass');
49235                 var rowcfg = {};
49236                 for(var j = 0, len = rs.length; j < len; j++){
49237                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49238                     for(var i = 0; i < colCount; i++){
49239                         c = cs[i];
49240                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49241                         p.id = c.id;
49242                         p.css = p.attr = "";
49243                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49244                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49245                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49246                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49247                         }
49248                         var markup = ct.apply(p);
49249                         if(!c.locked){
49250                             cb+= markup;
49251                         }else{
49252                             lcb+= markup;
49253                         }
49254                     }
49255                     var alt = [];
49256                     if(stripe && ((rowIndex+1) % 2 == 0)){
49257                         alt.push("x-grid-row-alt")
49258                     }
49259                     if(r.dirty){
49260                         alt.push(  " x-grid-dirty-row");
49261                     }
49262                     rp.cells = lcb;
49263                     if(this.getRowClass){
49264                         alt.push(this.getRowClass(r, rowIndex));
49265                     }
49266                     if (hasListener) {
49267                         rowcfg = {
49268                              
49269                             record: r,
49270                             rowIndex : rowIndex,
49271                             rowClass : ''
49272                         }
49273                         this.grid.fireEvent('rowclass', this, rowcfg);
49274                         alt.push(rowcfg.rowClass);
49275                     }
49276                     rp.alt = alt.join(" ");
49277                     lbuf+= rt.apply(rp);
49278                     rp.cells = cb;
49279                     buf+=  rt.apply(rp);
49280                 }
49281                 return [lbuf, buf];
49282             } :
49283             function(cs, rs, ds, startRow, colCount, stripe){
49284                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49285                 // buffers
49286                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49287                 var hasListener = this.grid.hasListener('rowclass');
49288                 var rowcfg = {};
49289                 for(var j = 0, len = rs.length; j < len; j++){
49290                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49291                     for(var i = 0; i < colCount; i++){
49292                         c = cs[i];
49293                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49294                         p.id = c.id;
49295                         p.css = p.attr = "";
49296                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49297                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49298                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49299                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49300                         }
49301                         var markup = ct.apply(p);
49302                         if(!c.locked){
49303                             cb[cb.length] = markup;
49304                         }else{
49305                             lcb[lcb.length] = markup;
49306                         }
49307                     }
49308                     var alt = [];
49309                     if(stripe && ((rowIndex+1) % 2 == 0)){
49310                         alt.push( "x-grid-row-alt");
49311                     }
49312                     if(r.dirty){
49313                         alt.push(" x-grid-dirty-row");
49314                     }
49315                     rp.cells = lcb;
49316                     if(this.getRowClass){
49317                         alt.push( this.getRowClass(r, rowIndex));
49318                     }
49319                     if (hasListener) {
49320                         rowcfg = {
49321                              
49322                             record: r,
49323                             rowIndex : rowIndex,
49324                             rowClass : ''
49325                         }
49326                         this.grid.fireEvent('rowclass', this, rowcfg);
49327                         alt.push(rowcfg.rowClass);
49328                     }
49329                     rp.alt = alt.join(" ");
49330                     rp.cells = lcb.join("");
49331                     lbuf[lbuf.length] = rt.apply(rp);
49332                     rp.cells = cb.join("");
49333                     buf[buf.length] =  rt.apply(rp);
49334                 }
49335                 return [lbuf.join(""), buf.join("")];
49336             },
49337
49338     renderBody : function(){
49339         var markup = this.renderRows();
49340         var bt = this.templates.body;
49341         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49342     },
49343
49344     /**
49345      * Refreshes the grid
49346      * @param {Boolean} headersToo
49347      */
49348     refresh : function(headersToo){
49349         this.fireEvent("beforerefresh", this);
49350         this.grid.stopEditing();
49351         var result = this.renderBody();
49352         this.lockedBody.update(result[0]);
49353         this.mainBody.update(result[1]);
49354         if(headersToo === true){
49355             this.updateHeaders();
49356             this.updateColumns();
49357             this.updateSplitters();
49358             this.updateHeaderSortState();
49359         }
49360         this.syncRowHeights();
49361         this.layout();
49362         this.fireEvent("refresh", this);
49363     },
49364
49365     handleColumnMove : function(cm, oldIndex, newIndex){
49366         this.indexMap = null;
49367         var s = this.getScrollState();
49368         this.refresh(true);
49369         this.restoreScroll(s);
49370         this.afterMove(newIndex);
49371     },
49372
49373     afterMove : function(colIndex){
49374         if(this.enableMoveAnim && Roo.enableFx){
49375             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49376         }
49377         // if multisort - fix sortOrder, and reload..
49378         if (this.grid.dataSource.multiSort) {
49379             // the we can call sort again..
49380             var dm = this.grid.dataSource;
49381             var cm = this.grid.colModel;
49382             var so = [];
49383             for(var i = 0; i < cm.config.length; i++ ) {
49384                 
49385                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49386                     continue; // dont' bother, it's not in sort list or being set.
49387                 }
49388                 
49389                 so.push(cm.config[i].dataIndex);
49390             };
49391             dm.sortOrder = so;
49392             dm.load(dm.lastOptions);
49393             
49394             
49395         }
49396         
49397     },
49398
49399     updateCell : function(dm, rowIndex, dataIndex){
49400         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49401         if(typeof colIndex == "undefined"){ // not present in grid
49402             return;
49403         }
49404         var cm = this.grid.colModel;
49405         var cell = this.getCell(rowIndex, colIndex);
49406         var cellText = this.getCellText(rowIndex, colIndex);
49407
49408         var p = {
49409             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49410             id : cm.getColumnId(colIndex),
49411             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49412         };
49413         var renderer = cm.getRenderer(colIndex);
49414         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49415         if(typeof val == "undefined" || val === "") val = "&#160;";
49416         cellText.innerHTML = val;
49417         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49418         this.syncRowHeights(rowIndex, rowIndex);
49419     },
49420
49421     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49422         var maxWidth = 0;
49423         if(this.grid.autoSizeHeaders){
49424             var h = this.getHeaderCellMeasure(colIndex);
49425             maxWidth = Math.max(maxWidth, h.scrollWidth);
49426         }
49427         var tb, index;
49428         if(this.cm.isLocked(colIndex)){
49429             tb = this.getLockedTable();
49430             index = colIndex;
49431         }else{
49432             tb = this.getBodyTable();
49433             index = colIndex - this.cm.getLockedCount();
49434         }
49435         if(tb && tb.rows){
49436             var rows = tb.rows;
49437             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49438             for(var i = 0; i < stopIndex; i++){
49439                 var cell = rows[i].childNodes[index].firstChild;
49440                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49441             }
49442         }
49443         return maxWidth + /*margin for error in IE*/ 5;
49444     },
49445     /**
49446      * Autofit a column to its content.
49447      * @param {Number} colIndex
49448      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49449      */
49450      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49451          if(this.cm.isHidden(colIndex)){
49452              return; // can't calc a hidden column
49453          }
49454         if(forceMinSize){
49455             var cid = this.cm.getColumnId(colIndex);
49456             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49457            if(this.grid.autoSizeHeaders){
49458                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49459            }
49460         }
49461         var newWidth = this.calcColumnWidth(colIndex);
49462         this.cm.setColumnWidth(colIndex,
49463             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49464         if(!suppressEvent){
49465             this.grid.fireEvent("columnresize", colIndex, newWidth);
49466         }
49467     },
49468
49469     /**
49470      * Autofits all columns to their content and then expands to fit any extra space in the grid
49471      */
49472      autoSizeColumns : function(){
49473         var cm = this.grid.colModel;
49474         var colCount = cm.getColumnCount();
49475         for(var i = 0; i < colCount; i++){
49476             this.autoSizeColumn(i, true, true);
49477         }
49478         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49479             this.fitColumns();
49480         }else{
49481             this.updateColumns();
49482             this.layout();
49483         }
49484     },
49485
49486     /**
49487      * Autofits all columns to the grid's width proportionate with their current size
49488      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49489      */
49490     fitColumns : function(reserveScrollSpace){
49491         var cm = this.grid.colModel;
49492         var colCount = cm.getColumnCount();
49493         var cols = [];
49494         var width = 0;
49495         var i, w;
49496         for (i = 0; i < colCount; i++){
49497             if(!cm.isHidden(i) && !cm.isFixed(i)){
49498                 w = cm.getColumnWidth(i);
49499                 cols.push(i);
49500                 cols.push(w);
49501                 width += w;
49502             }
49503         }
49504         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49505         if(reserveScrollSpace){
49506             avail -= 17;
49507         }
49508         var frac = (avail - cm.getTotalWidth())/width;
49509         while (cols.length){
49510             w = cols.pop();
49511             i = cols.pop();
49512             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49513         }
49514         this.updateColumns();
49515         this.layout();
49516     },
49517
49518     onRowSelect : function(rowIndex){
49519         var row = this.getRowComposite(rowIndex);
49520         row.addClass("x-grid-row-selected");
49521     },
49522
49523     onRowDeselect : function(rowIndex){
49524         var row = this.getRowComposite(rowIndex);
49525         row.removeClass("x-grid-row-selected");
49526     },
49527
49528     onCellSelect : function(row, col){
49529         var cell = this.getCell(row, col);
49530         if(cell){
49531             Roo.fly(cell).addClass("x-grid-cell-selected");
49532         }
49533     },
49534
49535     onCellDeselect : function(row, col){
49536         var cell = this.getCell(row, col);
49537         if(cell){
49538             Roo.fly(cell).removeClass("x-grid-cell-selected");
49539         }
49540     },
49541
49542     updateHeaderSortState : function(){
49543         
49544         // sort state can be single { field: xxx, direction : yyy}
49545         // or   { xxx=>ASC , yyy : DESC ..... }
49546         
49547         var mstate = {};
49548         if (!this.ds.multiSort) { 
49549             var state = this.ds.getSortState();
49550             if(!state){
49551                 return;
49552             }
49553             mstate[state.field] = state.direction;
49554             // FIXME... - this is not used here.. but might be elsewhere..
49555             this.sortState = state;
49556             
49557         } else {
49558             mstate = this.ds.sortToggle;
49559         }
49560         //remove existing sort classes..
49561         
49562         var sc = this.sortClasses;
49563         var hds = this.el.select(this.headerSelector).removeClass(sc);
49564         
49565         for(var f in mstate) {
49566         
49567             var sortColumn = this.cm.findColumnIndex(f);
49568             
49569             if(sortColumn != -1){
49570                 var sortDir = mstate[f];        
49571                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49572             }
49573         }
49574         
49575          
49576         
49577     },
49578
49579
49580     handleHeaderClick : function(g, index){
49581         if(this.headersDisabled){
49582             return;
49583         }
49584         var dm = g.dataSource, cm = g.colModel;
49585         if(!cm.isSortable(index)){
49586             return;
49587         }
49588         g.stopEditing();
49589         
49590         if (dm.multiSort) {
49591             // update the sortOrder
49592             var so = [];
49593             for(var i = 0; i < cm.config.length; i++ ) {
49594                 
49595                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49596                     continue; // dont' bother, it's not in sort list or being set.
49597                 }
49598                 
49599                 so.push(cm.config[i].dataIndex);
49600             };
49601             dm.sortOrder = so;
49602         }
49603         
49604         
49605         dm.sort(cm.getDataIndex(index));
49606     },
49607
49608
49609     destroy : function(){
49610         if(this.colMenu){
49611             this.colMenu.removeAll();
49612             Roo.menu.MenuMgr.unregister(this.colMenu);
49613             this.colMenu.getEl().remove();
49614             delete this.colMenu;
49615         }
49616         if(this.hmenu){
49617             this.hmenu.removeAll();
49618             Roo.menu.MenuMgr.unregister(this.hmenu);
49619             this.hmenu.getEl().remove();
49620             delete this.hmenu;
49621         }
49622         if(this.grid.enableColumnMove){
49623             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49624             if(dds){
49625                 for(var dd in dds){
49626                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49627                         var elid = dds[dd].dragElId;
49628                         dds[dd].unreg();
49629                         Roo.get(elid).remove();
49630                     } else if(dds[dd].config.isTarget){
49631                         dds[dd].proxyTop.remove();
49632                         dds[dd].proxyBottom.remove();
49633                         dds[dd].unreg();
49634                     }
49635                     if(Roo.dd.DDM.locationCache[dd]){
49636                         delete Roo.dd.DDM.locationCache[dd];
49637                     }
49638                 }
49639                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49640             }
49641         }
49642         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49643         this.bind(null, null);
49644         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49645     },
49646
49647     handleLockChange : function(){
49648         this.refresh(true);
49649     },
49650
49651     onDenyColumnLock : function(){
49652
49653     },
49654
49655     onDenyColumnHide : function(){
49656
49657     },
49658
49659     handleHdMenuClick : function(item){
49660         var index = this.hdCtxIndex;
49661         var cm = this.cm, ds = this.ds;
49662         switch(item.id){
49663             case "asc":
49664                 ds.sort(cm.getDataIndex(index), "ASC");
49665                 break;
49666             case "desc":
49667                 ds.sort(cm.getDataIndex(index), "DESC");
49668                 break;
49669             case "lock":
49670                 var lc = cm.getLockedCount();
49671                 if(cm.getColumnCount(true) <= lc+1){
49672                     this.onDenyColumnLock();
49673                     return;
49674                 }
49675                 if(lc != index){
49676                     cm.setLocked(index, true, true);
49677                     cm.moveColumn(index, lc);
49678                     this.grid.fireEvent("columnmove", index, lc);
49679                 }else{
49680                     cm.setLocked(index, true);
49681                 }
49682             break;
49683             case "unlock":
49684                 var lc = cm.getLockedCount();
49685                 if((lc-1) != index){
49686                     cm.setLocked(index, false, true);
49687                     cm.moveColumn(index, lc-1);
49688                     this.grid.fireEvent("columnmove", index, lc-1);
49689                 }else{
49690                     cm.setLocked(index, false);
49691                 }
49692             break;
49693             default:
49694                 index = cm.getIndexById(item.id.substr(4));
49695                 if(index != -1){
49696                     if(item.checked && cm.getColumnCount(true) <= 1){
49697                         this.onDenyColumnHide();
49698                         return false;
49699                     }
49700                     cm.setHidden(index, item.checked);
49701                 }
49702         }
49703         return true;
49704     },
49705
49706     beforeColMenuShow : function(){
49707         var cm = this.cm,  colCount = cm.getColumnCount();
49708         this.colMenu.removeAll();
49709         for(var i = 0; i < colCount; i++){
49710             this.colMenu.add(new Roo.menu.CheckItem({
49711                 id: "col-"+cm.getColumnId(i),
49712                 text: cm.getColumnHeader(i),
49713                 checked: !cm.isHidden(i),
49714                 hideOnClick:false
49715             }));
49716         }
49717     },
49718
49719     handleHdCtx : function(g, index, e){
49720         e.stopEvent();
49721         var hd = this.getHeaderCell(index);
49722         this.hdCtxIndex = index;
49723         var ms = this.hmenu.items, cm = this.cm;
49724         ms.get("asc").setDisabled(!cm.isSortable(index));
49725         ms.get("desc").setDisabled(!cm.isSortable(index));
49726         if(this.grid.enableColLock !== false){
49727             ms.get("lock").setDisabled(cm.isLocked(index));
49728             ms.get("unlock").setDisabled(!cm.isLocked(index));
49729         }
49730         this.hmenu.show(hd, "tl-bl");
49731     },
49732
49733     handleHdOver : function(e){
49734         var hd = this.findHeaderCell(e.getTarget());
49735         if(hd && !this.headersDisabled){
49736             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49737                this.fly(hd).addClass("x-grid-hd-over");
49738             }
49739         }
49740     },
49741
49742     handleHdOut : function(e){
49743         var hd = this.findHeaderCell(e.getTarget());
49744         if(hd){
49745             this.fly(hd).removeClass("x-grid-hd-over");
49746         }
49747     },
49748
49749     handleSplitDblClick : function(e, t){
49750         var i = this.getCellIndex(t);
49751         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49752             this.autoSizeColumn(i, true);
49753             this.layout();
49754         }
49755     },
49756
49757     render : function(){
49758
49759         var cm = this.cm;
49760         var colCount = cm.getColumnCount();
49761
49762         if(this.grid.monitorWindowResize === true){
49763             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49764         }
49765         var header = this.renderHeaders();
49766         var body = this.templates.body.apply({rows:""});
49767         var html = this.templates.master.apply({
49768             lockedBody: body,
49769             body: body,
49770             lockedHeader: header[0],
49771             header: header[1]
49772         });
49773
49774         //this.updateColumns();
49775
49776         this.grid.getGridEl().dom.innerHTML = html;
49777
49778         this.initElements();
49779         
49780         // a kludge to fix the random scolling effect in webkit
49781         this.el.on("scroll", function() {
49782             this.el.dom.scrollTop=0; // hopefully not recursive..
49783         },this);
49784
49785         this.scroller.on("scroll", this.handleScroll, this);
49786         this.lockedBody.on("mousewheel", this.handleWheel, this);
49787         this.mainBody.on("mousewheel", this.handleWheel, this);
49788
49789         this.mainHd.on("mouseover", this.handleHdOver, this);
49790         this.mainHd.on("mouseout", this.handleHdOut, this);
49791         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49792                 {delegate: "."+this.splitClass});
49793
49794         this.lockedHd.on("mouseover", this.handleHdOver, this);
49795         this.lockedHd.on("mouseout", this.handleHdOut, this);
49796         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49797                 {delegate: "."+this.splitClass});
49798
49799         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49800             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49801         }
49802
49803         this.updateSplitters();
49804
49805         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49806             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49807             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49808         }
49809
49810         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49811             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49812             this.hmenu.add(
49813                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49814                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49815             );
49816             if(this.grid.enableColLock !== false){
49817                 this.hmenu.add('-',
49818                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49819                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49820                 );
49821             }
49822             if(this.grid.enableColumnHide !== false){
49823
49824                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49825                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49826                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49827
49828                 this.hmenu.add('-',
49829                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49830                 );
49831             }
49832             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49833
49834             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49835         }
49836
49837         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49838             this.dd = new Roo.grid.GridDragZone(this.grid, {
49839                 ddGroup : this.grid.ddGroup || 'GridDD'
49840             });
49841         }
49842
49843         /*
49844         for(var i = 0; i < colCount; i++){
49845             if(cm.isHidden(i)){
49846                 this.hideColumn(i);
49847             }
49848             if(cm.config[i].align){
49849                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49850                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49851             }
49852         }*/
49853         
49854         this.updateHeaderSortState();
49855
49856         this.beforeInitialResize();
49857         this.layout(true);
49858
49859         // two part rendering gives faster view to the user
49860         this.renderPhase2.defer(1, this);
49861     },
49862
49863     renderPhase2 : function(){
49864         // render the rows now
49865         this.refresh();
49866         if(this.grid.autoSizeColumns){
49867             this.autoSizeColumns();
49868         }
49869     },
49870
49871     beforeInitialResize : function(){
49872
49873     },
49874
49875     onColumnSplitterMoved : function(i, w){
49876         this.userResized = true;
49877         var cm = this.grid.colModel;
49878         cm.setColumnWidth(i, w, true);
49879         var cid = cm.getColumnId(i);
49880         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49881         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49882         this.updateSplitters();
49883         this.layout();
49884         this.grid.fireEvent("columnresize", i, w);
49885     },
49886
49887     syncRowHeights : function(startIndex, endIndex){
49888         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49889             startIndex = startIndex || 0;
49890             var mrows = this.getBodyTable().rows;
49891             var lrows = this.getLockedTable().rows;
49892             var len = mrows.length-1;
49893             endIndex = Math.min(endIndex || len, len);
49894             for(var i = startIndex; i <= endIndex; i++){
49895                 var m = mrows[i], l = lrows[i];
49896                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49897                 m.style.height = l.style.height = h + "px";
49898             }
49899         }
49900     },
49901
49902     layout : function(initialRender, is2ndPass){
49903         var g = this.grid;
49904         var auto = g.autoHeight;
49905         var scrollOffset = 16;
49906         var c = g.getGridEl(), cm = this.cm,
49907                 expandCol = g.autoExpandColumn,
49908                 gv = this;
49909         //c.beginMeasure();
49910
49911         if(!c.dom.offsetWidth){ // display:none?
49912             if(initialRender){
49913                 this.lockedWrap.show();
49914                 this.mainWrap.show();
49915             }
49916             return;
49917         }
49918
49919         var hasLock = this.cm.isLocked(0);
49920
49921         var tbh = this.headerPanel.getHeight();
49922         var bbh = this.footerPanel.getHeight();
49923
49924         if(auto){
49925             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49926             var newHeight = ch + c.getBorderWidth("tb");
49927             if(g.maxHeight){
49928                 newHeight = Math.min(g.maxHeight, newHeight);
49929             }
49930             c.setHeight(newHeight);
49931         }
49932
49933         if(g.autoWidth){
49934             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49935         }
49936
49937         var s = this.scroller;
49938
49939         var csize = c.getSize(true);
49940
49941         this.el.setSize(csize.width, csize.height);
49942
49943         this.headerPanel.setWidth(csize.width);
49944         this.footerPanel.setWidth(csize.width);
49945
49946         var hdHeight = this.mainHd.getHeight();
49947         var vw = csize.width;
49948         var vh = csize.height - (tbh + bbh);
49949
49950         s.setSize(vw, vh);
49951
49952         var bt = this.getBodyTable();
49953         var ltWidth = hasLock ?
49954                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49955
49956         var scrollHeight = bt.offsetHeight;
49957         var scrollWidth = ltWidth + bt.offsetWidth;
49958         var vscroll = false, hscroll = false;
49959
49960         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49961
49962         var lw = this.lockedWrap, mw = this.mainWrap;
49963         var lb = this.lockedBody, mb = this.mainBody;
49964
49965         setTimeout(function(){
49966             var t = s.dom.offsetTop;
49967             var w = s.dom.clientWidth,
49968                 h = s.dom.clientHeight;
49969
49970             lw.setTop(t);
49971             lw.setSize(ltWidth, h);
49972
49973             mw.setLeftTop(ltWidth, t);
49974             mw.setSize(w-ltWidth, h);
49975
49976             lb.setHeight(h-hdHeight);
49977             mb.setHeight(h-hdHeight);
49978
49979             if(is2ndPass !== true && !gv.userResized && expandCol){
49980                 // high speed resize without full column calculation
49981                 
49982                 var ci = cm.getIndexById(expandCol);
49983                 if (ci < 0) {
49984                     ci = cm.findColumnIndex(expandCol);
49985                 }
49986                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49987                 var expandId = cm.getColumnId(ci);
49988                 var  tw = cm.getTotalWidth(false);
49989                 var currentWidth = cm.getColumnWidth(ci);
49990                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49991                 if(currentWidth != cw){
49992                     cm.setColumnWidth(ci, cw, true);
49993                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49994                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49995                     gv.updateSplitters();
49996                     gv.layout(false, true);
49997                 }
49998             }
49999
50000             if(initialRender){
50001                 lw.show();
50002                 mw.show();
50003             }
50004             //c.endMeasure();
50005         }, 10);
50006     },
50007
50008     onWindowResize : function(){
50009         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50010             return;
50011         }
50012         this.layout();
50013     },
50014
50015     appendFooter : function(parentEl){
50016         return null;
50017     },
50018
50019     sortAscText : "Sort Ascending",
50020     sortDescText : "Sort Descending",
50021     lockText : "Lock Column",
50022     unlockText : "Unlock Column",
50023     columnsText : "Columns"
50024 });
50025
50026
50027 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50028     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50029     this.proxy.el.addClass('x-grid3-col-dd');
50030 };
50031
50032 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50033     handleMouseDown : function(e){
50034
50035     },
50036
50037     callHandleMouseDown : function(e){
50038         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50039     }
50040 });
50041 /*
50042  * Based on:
50043  * Ext JS Library 1.1.1
50044  * Copyright(c) 2006-2007, Ext JS, LLC.
50045  *
50046  * Originally Released Under LGPL - original licence link has changed is not relivant.
50047  *
50048  * Fork - LGPL
50049  * <script type="text/javascript">
50050  */
50051  
50052 // private
50053 // This is a support class used internally by the Grid components
50054 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50055     this.grid = grid;
50056     this.view = grid.getView();
50057     this.proxy = this.view.resizeProxy;
50058     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50059         "gridSplitters" + this.grid.getGridEl().id, {
50060         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50061     });
50062     this.setHandleElId(Roo.id(hd));
50063     this.setOuterHandleElId(Roo.id(hd2));
50064     this.scroll = false;
50065 };
50066 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50067     fly: Roo.Element.fly,
50068
50069     b4StartDrag : function(x, y){
50070         this.view.headersDisabled = true;
50071         this.proxy.setHeight(this.view.mainWrap.getHeight());
50072         var w = this.cm.getColumnWidth(this.cellIndex);
50073         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50074         this.resetConstraints();
50075         this.setXConstraint(minw, 1000);
50076         this.setYConstraint(0, 0);
50077         this.minX = x - minw;
50078         this.maxX = x + 1000;
50079         this.startPos = x;
50080         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50081     },
50082
50083
50084     handleMouseDown : function(e){
50085         ev = Roo.EventObject.setEvent(e);
50086         var t = this.fly(ev.getTarget());
50087         if(t.hasClass("x-grid-split")){
50088             this.cellIndex = this.view.getCellIndex(t.dom);
50089             this.split = t.dom;
50090             this.cm = this.grid.colModel;
50091             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50092                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50093             }
50094         }
50095     },
50096
50097     endDrag : function(e){
50098         this.view.headersDisabled = false;
50099         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50100         var diff = endX - this.startPos;
50101         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50102     },
50103
50104     autoOffset : function(){
50105         this.setDelta(0,0);
50106     }
50107 });/*
50108  * Based on:
50109  * Ext JS Library 1.1.1
50110  * Copyright(c) 2006-2007, Ext JS, LLC.
50111  *
50112  * Originally Released Under LGPL - original licence link has changed is not relivant.
50113  *
50114  * Fork - LGPL
50115  * <script type="text/javascript">
50116  */
50117  
50118 // private
50119 // This is a support class used internally by the Grid components
50120 Roo.grid.GridDragZone = function(grid, config){
50121     this.view = grid.getView();
50122     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50123     if(this.view.lockedBody){
50124         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50125         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50126     }
50127     this.scroll = false;
50128     this.grid = grid;
50129     this.ddel = document.createElement('div');
50130     this.ddel.className = 'x-grid-dd-wrap';
50131 };
50132
50133 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50134     ddGroup : "GridDD",
50135
50136     getDragData : function(e){
50137         var t = Roo.lib.Event.getTarget(e);
50138         var rowIndex = this.view.findRowIndex(t);
50139         if(rowIndex !== false){
50140             var sm = this.grid.selModel;
50141             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50142               //  sm.mouseDown(e, t);
50143             //}
50144             if (e.hasModifier()){
50145                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50146             }
50147             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50148         }
50149         return false;
50150     },
50151
50152     onInitDrag : function(e){
50153         var data = this.dragData;
50154         this.ddel.innerHTML = this.grid.getDragDropText();
50155         this.proxy.update(this.ddel);
50156         // fire start drag?
50157     },
50158
50159     afterRepair : function(){
50160         this.dragging = false;
50161     },
50162
50163     getRepairXY : function(e, data){
50164         return false;
50165     },
50166
50167     onEndDrag : function(data, e){
50168         // fire end drag?
50169     },
50170
50171     onValidDrop : function(dd, e, id){
50172         // fire drag drop?
50173         this.hideProxy();
50174     },
50175
50176     beforeInvalidDrop : function(e, id){
50177
50178     }
50179 });/*
50180  * Based on:
50181  * Ext JS Library 1.1.1
50182  * Copyright(c) 2006-2007, Ext JS, LLC.
50183  *
50184  * Originally Released Under LGPL - original licence link has changed is not relivant.
50185  *
50186  * Fork - LGPL
50187  * <script type="text/javascript">
50188  */
50189  
50190
50191 /**
50192  * @class Roo.grid.ColumnModel
50193  * @extends Roo.util.Observable
50194  * This is the default implementation of a ColumnModel used by the Grid. It defines
50195  * the columns in the grid.
50196  * <br>Usage:<br>
50197  <pre><code>
50198  var colModel = new Roo.grid.ColumnModel([
50199         {header: "Ticker", width: 60, sortable: true, locked: true},
50200         {header: "Company Name", width: 150, sortable: true},
50201         {header: "Market Cap.", width: 100, sortable: true},
50202         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50203         {header: "Employees", width: 100, sortable: true, resizable: false}
50204  ]);
50205  </code></pre>
50206  * <p>
50207  
50208  * The config options listed for this class are options which may appear in each
50209  * individual column definition.
50210  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50211  * @constructor
50212  * @param {Object} config An Array of column config objects. See this class's
50213  * config objects for details.
50214 */
50215 Roo.grid.ColumnModel = function(config){
50216         /**
50217      * The config passed into the constructor
50218      */
50219     this.config = config;
50220     this.lookup = {};
50221
50222     // if no id, create one
50223     // if the column does not have a dataIndex mapping,
50224     // map it to the order it is in the config
50225     for(var i = 0, len = config.length; i < len; i++){
50226         var c = config[i];
50227         if(typeof c.dataIndex == "undefined"){
50228             c.dataIndex = i;
50229         }
50230         if(typeof c.renderer == "string"){
50231             c.renderer = Roo.util.Format[c.renderer];
50232         }
50233         if(typeof c.id == "undefined"){
50234             c.id = Roo.id();
50235         }
50236         if(c.editor && c.editor.xtype){
50237             c.editor  = Roo.factory(c.editor, Roo.grid);
50238         }
50239         if(c.editor && c.editor.isFormField){
50240             c.editor = new Roo.grid.GridEditor(c.editor);
50241         }
50242         this.lookup[c.id] = c;
50243     }
50244
50245     /**
50246      * The width of columns which have no width specified (defaults to 100)
50247      * @type Number
50248      */
50249     this.defaultWidth = 100;
50250
50251     /**
50252      * Default sortable of columns which have no sortable specified (defaults to false)
50253      * @type Boolean
50254      */
50255     this.defaultSortable = false;
50256
50257     this.addEvents({
50258         /**
50259              * @event widthchange
50260              * Fires when the width of a column changes.
50261              * @param {ColumnModel} this
50262              * @param {Number} columnIndex The column index
50263              * @param {Number} newWidth The new width
50264              */
50265             "widthchange": true,
50266         /**
50267              * @event headerchange
50268              * Fires when the text of a header changes.
50269              * @param {ColumnModel} this
50270              * @param {Number} columnIndex The column index
50271              * @param {Number} newText The new header text
50272              */
50273             "headerchange": true,
50274         /**
50275              * @event hiddenchange
50276              * Fires when a column is hidden or "unhidden".
50277              * @param {ColumnModel} this
50278              * @param {Number} columnIndex The column index
50279              * @param {Boolean} hidden true if hidden, false otherwise
50280              */
50281             "hiddenchange": true,
50282             /**
50283          * @event columnmoved
50284          * Fires when a column is moved.
50285          * @param {ColumnModel} this
50286          * @param {Number} oldIndex
50287          * @param {Number} newIndex
50288          */
50289         "columnmoved" : true,
50290         /**
50291          * @event columlockchange
50292          * Fires when a column's locked state is changed
50293          * @param {ColumnModel} this
50294          * @param {Number} colIndex
50295          * @param {Boolean} locked true if locked
50296          */
50297         "columnlockchange" : true
50298     });
50299     Roo.grid.ColumnModel.superclass.constructor.call(this);
50300 };
50301 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50302     /**
50303      * @cfg {String} header The header text to display in the Grid view.
50304      */
50305     /**
50306      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50307      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50308      * specified, the column's index is used as an index into the Record's data Array.
50309      */
50310     /**
50311      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50312      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50313      */
50314     /**
50315      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50316      * Defaults to the value of the {@link #defaultSortable} property.
50317      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50318      */
50319     /**
50320      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50321      */
50322     /**
50323      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50324      */
50325     /**
50326      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50327      */
50328     /**
50329      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50330      */
50331     /**
50332      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50333      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50334      * default renderer uses the raw data value.
50335      */
50336        /**
50337      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50338      */
50339     /**
50340      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50341      */
50342
50343     /**
50344      * Returns the id of the column at the specified index.
50345      * @param {Number} index The column index
50346      * @return {String} the id
50347      */
50348     getColumnId : function(index){
50349         return this.config[index].id;
50350     },
50351
50352     /**
50353      * Returns the column for a specified id.
50354      * @param {String} id The column id
50355      * @return {Object} the column
50356      */
50357     getColumnById : function(id){
50358         return this.lookup[id];
50359     },
50360
50361     
50362     /**
50363      * Returns the column for a specified dataIndex.
50364      * @param {String} dataIndex The column dataIndex
50365      * @return {Object|Boolean} the column or false if not found
50366      */
50367     getColumnByDataIndex: function(dataIndex){
50368         var index = this.findColumnIndex(dataIndex);
50369         return index > -1 ? this.config[index] : false;
50370     },
50371     
50372     /**
50373      * Returns the index for a specified column id.
50374      * @param {String} id The column id
50375      * @return {Number} the index, or -1 if not found
50376      */
50377     getIndexById : function(id){
50378         for(var i = 0, len = this.config.length; i < len; i++){
50379             if(this.config[i].id == id){
50380                 return i;
50381             }
50382         }
50383         return -1;
50384     },
50385     
50386     /**
50387      * Returns the index for a specified column dataIndex.
50388      * @param {String} dataIndex The column dataIndex
50389      * @return {Number} the index, or -1 if not found
50390      */
50391     
50392     findColumnIndex : function(dataIndex){
50393         for(var i = 0, len = this.config.length; i < len; i++){
50394             if(this.config[i].dataIndex == dataIndex){
50395                 return i;
50396             }
50397         }
50398         return -1;
50399     },
50400     
50401     
50402     moveColumn : function(oldIndex, newIndex){
50403         var c = this.config[oldIndex];
50404         this.config.splice(oldIndex, 1);
50405         this.config.splice(newIndex, 0, c);
50406         this.dataMap = null;
50407         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50408     },
50409
50410     isLocked : function(colIndex){
50411         return this.config[colIndex].locked === true;
50412     },
50413
50414     setLocked : function(colIndex, value, suppressEvent){
50415         if(this.isLocked(colIndex) == value){
50416             return;
50417         }
50418         this.config[colIndex].locked = value;
50419         if(!suppressEvent){
50420             this.fireEvent("columnlockchange", this, colIndex, value);
50421         }
50422     },
50423
50424     getTotalLockedWidth : function(){
50425         var totalWidth = 0;
50426         for(var i = 0; i < this.config.length; i++){
50427             if(this.isLocked(i) && !this.isHidden(i)){
50428                 this.totalWidth += this.getColumnWidth(i);
50429             }
50430         }
50431         return totalWidth;
50432     },
50433
50434     getLockedCount : function(){
50435         for(var i = 0, len = this.config.length; i < len; i++){
50436             if(!this.isLocked(i)){
50437                 return i;
50438             }
50439         }
50440     },
50441
50442     /**
50443      * Returns the number of columns.
50444      * @return {Number}
50445      */
50446     getColumnCount : function(visibleOnly){
50447         if(visibleOnly === true){
50448             var c = 0;
50449             for(var i = 0, len = this.config.length; i < len; i++){
50450                 if(!this.isHidden(i)){
50451                     c++;
50452                 }
50453             }
50454             return c;
50455         }
50456         return this.config.length;
50457     },
50458
50459     /**
50460      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50461      * @param {Function} fn
50462      * @param {Object} scope (optional)
50463      * @return {Array} result
50464      */
50465     getColumnsBy : function(fn, scope){
50466         var r = [];
50467         for(var i = 0, len = this.config.length; i < len; i++){
50468             var c = this.config[i];
50469             if(fn.call(scope||this, c, i) === true){
50470                 r[r.length] = c;
50471             }
50472         }
50473         return r;
50474     },
50475
50476     /**
50477      * Returns true if the specified column is sortable.
50478      * @param {Number} col The column index
50479      * @return {Boolean}
50480      */
50481     isSortable : function(col){
50482         if(typeof this.config[col].sortable == "undefined"){
50483             return this.defaultSortable;
50484         }
50485         return this.config[col].sortable;
50486     },
50487
50488     /**
50489      * Returns the rendering (formatting) function defined for the column.
50490      * @param {Number} col The column index.
50491      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50492      */
50493     getRenderer : function(col){
50494         if(!this.config[col].renderer){
50495             return Roo.grid.ColumnModel.defaultRenderer;
50496         }
50497         return this.config[col].renderer;
50498     },
50499
50500     /**
50501      * Sets the rendering (formatting) function for a column.
50502      * @param {Number} col The column index
50503      * @param {Function} fn The function to use to process the cell's raw data
50504      * to return HTML markup for the grid view. The render function is called with
50505      * the following parameters:<ul>
50506      * <li>Data value.</li>
50507      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50508      * <li>css A CSS style string to apply to the table cell.</li>
50509      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50510      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50511      * <li>Row index</li>
50512      * <li>Column index</li>
50513      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50514      */
50515     setRenderer : function(col, fn){
50516         this.config[col].renderer = fn;
50517     },
50518
50519     /**
50520      * Returns the width for the specified column.
50521      * @param {Number} col The column index
50522      * @return {Number}
50523      */
50524     getColumnWidth : function(col){
50525         return this.config[col].width * 1 || this.defaultWidth;
50526     },
50527
50528     /**
50529      * Sets the width for a column.
50530      * @param {Number} col The column index
50531      * @param {Number} width The new width
50532      */
50533     setColumnWidth : function(col, width, suppressEvent){
50534         this.config[col].width = width;
50535         this.totalWidth = null;
50536         if(!suppressEvent){
50537              this.fireEvent("widthchange", this, col, width);
50538         }
50539     },
50540
50541     /**
50542      * Returns the total width of all columns.
50543      * @param {Boolean} includeHidden True to include hidden column widths
50544      * @return {Number}
50545      */
50546     getTotalWidth : function(includeHidden){
50547         if(!this.totalWidth){
50548             this.totalWidth = 0;
50549             for(var i = 0, len = this.config.length; i < len; i++){
50550                 if(includeHidden || !this.isHidden(i)){
50551                     this.totalWidth += this.getColumnWidth(i);
50552                 }
50553             }
50554         }
50555         return this.totalWidth;
50556     },
50557
50558     /**
50559      * Returns the header for the specified column.
50560      * @param {Number} col The column index
50561      * @return {String}
50562      */
50563     getColumnHeader : function(col){
50564         return this.config[col].header;
50565     },
50566
50567     /**
50568      * Sets the header for a column.
50569      * @param {Number} col The column index
50570      * @param {String} header The new header
50571      */
50572     setColumnHeader : function(col, header){
50573         this.config[col].header = header;
50574         this.fireEvent("headerchange", this, col, header);
50575     },
50576
50577     /**
50578      * Returns the tooltip for the specified column.
50579      * @param {Number} col The column index
50580      * @return {String}
50581      */
50582     getColumnTooltip : function(col){
50583             return this.config[col].tooltip;
50584     },
50585     /**
50586      * Sets the tooltip for a column.
50587      * @param {Number} col The column index
50588      * @param {String} tooltip The new tooltip
50589      */
50590     setColumnTooltip : function(col, tooltip){
50591             this.config[col].tooltip = tooltip;
50592     },
50593
50594     /**
50595      * Returns the dataIndex for the specified column.
50596      * @param {Number} col The column index
50597      * @return {Number}
50598      */
50599     getDataIndex : function(col){
50600         return this.config[col].dataIndex;
50601     },
50602
50603     /**
50604      * Sets the dataIndex for a column.
50605      * @param {Number} col The column index
50606      * @param {Number} dataIndex The new dataIndex
50607      */
50608     setDataIndex : function(col, dataIndex){
50609         this.config[col].dataIndex = dataIndex;
50610     },
50611
50612     
50613     
50614     /**
50615      * Returns true if the cell is editable.
50616      * @param {Number} colIndex The column index
50617      * @param {Number} rowIndex The row index
50618      * @return {Boolean}
50619      */
50620     isCellEditable : function(colIndex, rowIndex){
50621         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50622     },
50623
50624     /**
50625      * Returns the editor defined for the cell/column.
50626      * return false or null to disable editing.
50627      * @param {Number} colIndex The column index
50628      * @param {Number} rowIndex The row index
50629      * @return {Object}
50630      */
50631     getCellEditor : function(colIndex, rowIndex){
50632         return this.config[colIndex].editor;
50633     },
50634
50635     /**
50636      * Sets if a column is editable.
50637      * @param {Number} col The column index
50638      * @param {Boolean} editable True if the column is editable
50639      */
50640     setEditable : function(col, editable){
50641         this.config[col].editable = editable;
50642     },
50643
50644
50645     /**
50646      * Returns true if the column is hidden.
50647      * @param {Number} colIndex The column index
50648      * @return {Boolean}
50649      */
50650     isHidden : function(colIndex){
50651         return this.config[colIndex].hidden;
50652     },
50653
50654
50655     /**
50656      * Returns true if the column width cannot be changed
50657      */
50658     isFixed : function(colIndex){
50659         return this.config[colIndex].fixed;
50660     },
50661
50662     /**
50663      * Returns true if the column can be resized
50664      * @return {Boolean}
50665      */
50666     isResizable : function(colIndex){
50667         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50668     },
50669     /**
50670      * Sets if a column is hidden.
50671      * @param {Number} colIndex The column index
50672      * @param {Boolean} hidden True if the column is hidden
50673      */
50674     setHidden : function(colIndex, hidden){
50675         this.config[colIndex].hidden = hidden;
50676         this.totalWidth = null;
50677         this.fireEvent("hiddenchange", this, colIndex, hidden);
50678     },
50679
50680     /**
50681      * Sets the editor for a column.
50682      * @param {Number} col The column index
50683      * @param {Object} editor The editor object
50684      */
50685     setEditor : function(col, editor){
50686         this.config[col].editor = editor;
50687     }
50688 });
50689
50690 Roo.grid.ColumnModel.defaultRenderer = function(value){
50691         if(typeof value == "string" && value.length < 1){
50692             return "&#160;";
50693         }
50694         return value;
50695 };
50696
50697 // Alias for backwards compatibility
50698 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50699 /*
50700  * Based on:
50701  * Ext JS Library 1.1.1
50702  * Copyright(c) 2006-2007, Ext JS, LLC.
50703  *
50704  * Originally Released Under LGPL - original licence link has changed is not relivant.
50705  *
50706  * Fork - LGPL
50707  * <script type="text/javascript">
50708  */
50709
50710 /**
50711  * @class Roo.grid.AbstractSelectionModel
50712  * @extends Roo.util.Observable
50713  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50714  * implemented by descendant classes.  This class should not be directly instantiated.
50715  * @constructor
50716  */
50717 Roo.grid.AbstractSelectionModel = function(){
50718     this.locked = false;
50719     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50720 };
50721
50722 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50723     /** @ignore Called by the grid automatically. Do not call directly. */
50724     init : function(grid){
50725         this.grid = grid;
50726         this.initEvents();
50727     },
50728
50729     /**
50730      * Locks the selections.
50731      */
50732     lock : function(){
50733         this.locked = true;
50734     },
50735
50736     /**
50737      * Unlocks the selections.
50738      */
50739     unlock : function(){
50740         this.locked = false;
50741     },
50742
50743     /**
50744      * Returns true if the selections are locked.
50745      * @return {Boolean}
50746      */
50747     isLocked : function(){
50748         return this.locked;
50749     }
50750 });/*
50751  * Based on:
50752  * Ext JS Library 1.1.1
50753  * Copyright(c) 2006-2007, Ext JS, LLC.
50754  *
50755  * Originally Released Under LGPL - original licence link has changed is not relivant.
50756  *
50757  * Fork - LGPL
50758  * <script type="text/javascript">
50759  */
50760 /**
50761  * @extends Roo.grid.AbstractSelectionModel
50762  * @class Roo.grid.RowSelectionModel
50763  * The default SelectionModel used by {@link Roo.grid.Grid}.
50764  * It supports multiple selections and keyboard selection/navigation. 
50765  * @constructor
50766  * @param {Object} config
50767  */
50768 Roo.grid.RowSelectionModel = function(config){
50769     Roo.apply(this, config);
50770     this.selections = new Roo.util.MixedCollection(false, function(o){
50771         return o.id;
50772     });
50773
50774     this.last = false;
50775     this.lastActive = false;
50776
50777     this.addEvents({
50778         /**
50779              * @event selectionchange
50780              * Fires when the selection changes
50781              * @param {SelectionModel} this
50782              */
50783             "selectionchange" : true,
50784         /**
50785              * @event afterselectionchange
50786              * Fires after the selection changes (eg. by key press or clicking)
50787              * @param {SelectionModel} this
50788              */
50789             "afterselectionchange" : true,
50790         /**
50791              * @event beforerowselect
50792              * Fires when a row is selected being selected, return false to cancel.
50793              * @param {SelectionModel} this
50794              * @param {Number} rowIndex The selected index
50795              * @param {Boolean} keepExisting False if other selections will be cleared
50796              */
50797             "beforerowselect" : true,
50798         /**
50799              * @event rowselect
50800              * Fires when a row is selected.
50801              * @param {SelectionModel} this
50802              * @param {Number} rowIndex The selected index
50803              * @param {Roo.data.Record} r The record
50804              */
50805             "rowselect" : true,
50806         /**
50807              * @event rowdeselect
50808              * Fires when a row is deselected.
50809              * @param {SelectionModel} this
50810              * @param {Number} rowIndex The selected index
50811              */
50812         "rowdeselect" : true
50813     });
50814     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50815     this.locked = false;
50816 };
50817
50818 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50819     /**
50820      * @cfg {Boolean} singleSelect
50821      * True to allow selection of only one row at a time (defaults to false)
50822      */
50823     singleSelect : false,
50824
50825     // private
50826     initEvents : function(){
50827
50828         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50829             this.grid.on("mousedown", this.handleMouseDown, this);
50830         }else{ // allow click to work like normal
50831             this.grid.on("rowclick", this.handleDragableRowClick, this);
50832         }
50833
50834         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50835             "up" : function(e){
50836                 if(!e.shiftKey){
50837                     this.selectPrevious(e.shiftKey);
50838                 }else if(this.last !== false && this.lastActive !== false){
50839                     var last = this.last;
50840                     this.selectRange(this.last,  this.lastActive-1);
50841                     this.grid.getView().focusRow(this.lastActive);
50842                     if(last !== false){
50843                         this.last = last;
50844                     }
50845                 }else{
50846                     this.selectFirstRow();
50847                 }
50848                 this.fireEvent("afterselectionchange", this);
50849             },
50850             "down" : function(e){
50851                 if(!e.shiftKey){
50852                     this.selectNext(e.shiftKey);
50853                 }else if(this.last !== false && this.lastActive !== false){
50854                     var last = this.last;
50855                     this.selectRange(this.last,  this.lastActive+1);
50856                     this.grid.getView().focusRow(this.lastActive);
50857                     if(last !== false){
50858                         this.last = last;
50859                     }
50860                 }else{
50861                     this.selectFirstRow();
50862                 }
50863                 this.fireEvent("afterselectionchange", this);
50864             },
50865             scope: this
50866         });
50867
50868         var view = this.grid.view;
50869         view.on("refresh", this.onRefresh, this);
50870         view.on("rowupdated", this.onRowUpdated, this);
50871         view.on("rowremoved", this.onRemove, this);
50872     },
50873
50874     // private
50875     onRefresh : function(){
50876         var ds = this.grid.dataSource, i, v = this.grid.view;
50877         var s = this.selections;
50878         s.each(function(r){
50879             if((i = ds.indexOfId(r.id)) != -1){
50880                 v.onRowSelect(i);
50881             }else{
50882                 s.remove(r);
50883             }
50884         });
50885     },
50886
50887     // private
50888     onRemove : function(v, index, r){
50889         this.selections.remove(r);
50890     },
50891
50892     // private
50893     onRowUpdated : function(v, index, r){
50894         if(this.isSelected(r)){
50895             v.onRowSelect(index);
50896         }
50897     },
50898
50899     /**
50900      * Select records.
50901      * @param {Array} records The records to select
50902      * @param {Boolean} keepExisting (optional) True to keep existing selections
50903      */
50904     selectRecords : function(records, keepExisting){
50905         if(!keepExisting){
50906             this.clearSelections();
50907         }
50908         var ds = this.grid.dataSource;
50909         for(var i = 0, len = records.length; i < len; i++){
50910             this.selectRow(ds.indexOf(records[i]), true);
50911         }
50912     },
50913
50914     /**
50915      * Gets the number of selected rows.
50916      * @return {Number}
50917      */
50918     getCount : function(){
50919         return this.selections.length;
50920     },
50921
50922     /**
50923      * Selects the first row in the grid.
50924      */
50925     selectFirstRow : function(){
50926         this.selectRow(0);
50927     },
50928
50929     /**
50930      * Select the last row.
50931      * @param {Boolean} keepExisting (optional) True to keep existing selections
50932      */
50933     selectLastRow : function(keepExisting){
50934         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50935     },
50936
50937     /**
50938      * Selects the row immediately following the last selected row.
50939      * @param {Boolean} keepExisting (optional) True to keep existing selections
50940      */
50941     selectNext : function(keepExisting){
50942         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50943             this.selectRow(this.last+1, keepExisting);
50944             this.grid.getView().focusRow(this.last);
50945         }
50946     },
50947
50948     /**
50949      * Selects the row that precedes the last selected row.
50950      * @param {Boolean} keepExisting (optional) True to keep existing selections
50951      */
50952     selectPrevious : function(keepExisting){
50953         if(this.last){
50954             this.selectRow(this.last-1, keepExisting);
50955             this.grid.getView().focusRow(this.last);
50956         }
50957     },
50958
50959     /**
50960      * Returns the selected records
50961      * @return {Array} Array of selected records
50962      */
50963     getSelections : function(){
50964         return [].concat(this.selections.items);
50965     },
50966
50967     /**
50968      * Returns the first selected record.
50969      * @return {Record}
50970      */
50971     getSelected : function(){
50972         return this.selections.itemAt(0);
50973     },
50974
50975
50976     /**
50977      * Clears all selections.
50978      */
50979     clearSelections : function(fast){
50980         if(this.locked) return;
50981         if(fast !== true){
50982             var ds = this.grid.dataSource;
50983             var s = this.selections;
50984             s.each(function(r){
50985                 this.deselectRow(ds.indexOfId(r.id));
50986             }, this);
50987             s.clear();
50988         }else{
50989             this.selections.clear();
50990         }
50991         this.last = false;
50992     },
50993
50994
50995     /**
50996      * Selects all rows.
50997      */
50998     selectAll : function(){
50999         if(this.locked) return;
51000         this.selections.clear();
51001         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51002             this.selectRow(i, true);
51003         }
51004     },
51005
51006     /**
51007      * Returns True if there is a selection.
51008      * @return {Boolean}
51009      */
51010     hasSelection : function(){
51011         return this.selections.length > 0;
51012     },
51013
51014     /**
51015      * Returns True if the specified row is selected.
51016      * @param {Number/Record} record The record or index of the record to check
51017      * @return {Boolean}
51018      */
51019     isSelected : function(index){
51020         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51021         return (r && this.selections.key(r.id) ? true : false);
51022     },
51023
51024     /**
51025      * Returns True if the specified record id is selected.
51026      * @param {String} id The id of record to check
51027      * @return {Boolean}
51028      */
51029     isIdSelected : function(id){
51030         return (this.selections.key(id) ? true : false);
51031     },
51032
51033     // private
51034     handleMouseDown : function(e, t){
51035         var view = this.grid.getView(), rowIndex;
51036         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51037             return;
51038         };
51039         if(e.shiftKey && this.last !== false){
51040             var last = this.last;
51041             this.selectRange(last, rowIndex, e.ctrlKey);
51042             this.last = last; // reset the last
51043             view.focusRow(rowIndex);
51044         }else{
51045             var isSelected = this.isSelected(rowIndex);
51046             if(e.button !== 0 && isSelected){
51047                 view.focusRow(rowIndex);
51048             }else if(e.ctrlKey && isSelected){
51049                 this.deselectRow(rowIndex);
51050             }else if(!isSelected){
51051                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51052                 view.focusRow(rowIndex);
51053             }
51054         }
51055         this.fireEvent("afterselectionchange", this);
51056     },
51057     // private
51058     handleDragableRowClick :  function(grid, rowIndex, e) 
51059     {
51060         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51061             this.selectRow(rowIndex, false);
51062             grid.view.focusRow(rowIndex);
51063              this.fireEvent("afterselectionchange", this);
51064         }
51065     },
51066     
51067     /**
51068      * Selects multiple rows.
51069      * @param {Array} rows Array of the indexes of the row to select
51070      * @param {Boolean} keepExisting (optional) True to keep existing selections
51071      */
51072     selectRows : function(rows, keepExisting){
51073         if(!keepExisting){
51074             this.clearSelections();
51075         }
51076         for(var i = 0, len = rows.length; i < len; i++){
51077             this.selectRow(rows[i], true);
51078         }
51079     },
51080
51081     /**
51082      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51083      * @param {Number} startRow The index of the first row in the range
51084      * @param {Number} endRow The index of the last row in the range
51085      * @param {Boolean} keepExisting (optional) True to retain existing selections
51086      */
51087     selectRange : function(startRow, endRow, keepExisting){
51088         if(this.locked) return;
51089         if(!keepExisting){
51090             this.clearSelections();
51091         }
51092         if(startRow <= endRow){
51093             for(var i = startRow; i <= endRow; i++){
51094                 this.selectRow(i, true);
51095             }
51096         }else{
51097             for(var i = startRow; i >= endRow; i--){
51098                 this.selectRow(i, true);
51099             }
51100         }
51101     },
51102
51103     /**
51104      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51105      * @param {Number} startRow The index of the first row in the range
51106      * @param {Number} endRow The index of the last row in the range
51107      */
51108     deselectRange : function(startRow, endRow, preventViewNotify){
51109         if(this.locked) return;
51110         for(var i = startRow; i <= endRow; i++){
51111             this.deselectRow(i, preventViewNotify);
51112         }
51113     },
51114
51115     /**
51116      * Selects a row.
51117      * @param {Number} row The index of the row to select
51118      * @param {Boolean} keepExisting (optional) True to keep existing selections
51119      */
51120     selectRow : function(index, keepExisting, preventViewNotify){
51121         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51122         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51123             if(!keepExisting || this.singleSelect){
51124                 this.clearSelections();
51125             }
51126             var r = this.grid.dataSource.getAt(index);
51127             this.selections.add(r);
51128             this.last = this.lastActive = index;
51129             if(!preventViewNotify){
51130                 this.grid.getView().onRowSelect(index);
51131             }
51132             this.fireEvent("rowselect", this, index, r);
51133             this.fireEvent("selectionchange", this);
51134         }
51135     },
51136
51137     /**
51138      * Deselects a row.
51139      * @param {Number} row The index of the row to deselect
51140      */
51141     deselectRow : function(index, preventViewNotify){
51142         if(this.locked) return;
51143         if(this.last == index){
51144             this.last = false;
51145         }
51146         if(this.lastActive == index){
51147             this.lastActive = false;
51148         }
51149         var r = this.grid.dataSource.getAt(index);
51150         this.selections.remove(r);
51151         if(!preventViewNotify){
51152             this.grid.getView().onRowDeselect(index);
51153         }
51154         this.fireEvent("rowdeselect", this, index);
51155         this.fireEvent("selectionchange", this);
51156     },
51157
51158     // private
51159     restoreLast : function(){
51160         if(this._last){
51161             this.last = this._last;
51162         }
51163     },
51164
51165     // private
51166     acceptsNav : function(row, col, cm){
51167         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51168     },
51169
51170     // private
51171     onEditorKey : function(field, e){
51172         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51173         if(k == e.TAB){
51174             e.stopEvent();
51175             ed.completeEdit();
51176             if(e.shiftKey){
51177                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51178             }else{
51179                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51180             }
51181         }else if(k == e.ENTER && !e.ctrlKey){
51182             e.stopEvent();
51183             ed.completeEdit();
51184             if(e.shiftKey){
51185                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51186             }else{
51187                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51188             }
51189         }else if(k == e.ESC){
51190             ed.cancelEdit();
51191         }
51192         if(newCell){
51193             g.startEditing(newCell[0], newCell[1]);
51194         }
51195     }
51196 });/*
51197  * Based on:
51198  * Ext JS Library 1.1.1
51199  * Copyright(c) 2006-2007, Ext JS, LLC.
51200  *
51201  * Originally Released Under LGPL - original licence link has changed is not relivant.
51202  *
51203  * Fork - LGPL
51204  * <script type="text/javascript">
51205  */
51206 /**
51207  * @class Roo.grid.CellSelectionModel
51208  * @extends Roo.grid.AbstractSelectionModel
51209  * This class provides the basic implementation for cell selection in a grid.
51210  * @constructor
51211  * @param {Object} config The object containing the configuration of this model.
51212  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51213  */
51214 Roo.grid.CellSelectionModel = function(config){
51215     Roo.apply(this, config);
51216
51217     this.selection = null;
51218
51219     this.addEvents({
51220         /**
51221              * @event beforerowselect
51222              * Fires before a cell is selected.
51223              * @param {SelectionModel} this
51224              * @param {Number} rowIndex The selected row index
51225              * @param {Number} colIndex The selected cell index
51226              */
51227             "beforecellselect" : true,
51228         /**
51229              * @event cellselect
51230              * Fires when a cell is selected.
51231              * @param {SelectionModel} this
51232              * @param {Number} rowIndex The selected row index
51233              * @param {Number} colIndex The selected cell index
51234              */
51235             "cellselect" : true,
51236         /**
51237              * @event selectionchange
51238              * Fires when the active selection changes.
51239              * @param {SelectionModel} this
51240              * @param {Object} selection null for no selection or an object (o) with two properties
51241                 <ul>
51242                 <li>o.record: the record object for the row the selection is in</li>
51243                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51244                 </ul>
51245              */
51246             "selectionchange" : true,
51247         /**
51248              * @event tabend
51249              * Fires when the tab (or enter) was pressed on the last editable cell
51250              * You can use this to trigger add new row.
51251              * @param {SelectionModel} this
51252              */
51253             "tabend" : true
51254     });
51255     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51256 };
51257
51258 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51259     
51260     enter_is_tab: false,
51261
51262     /** @ignore */
51263     initEvents : function(){
51264         this.grid.on("mousedown", this.handleMouseDown, this);
51265         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51266         var view = this.grid.view;
51267         view.on("refresh", this.onViewChange, this);
51268         view.on("rowupdated", this.onRowUpdated, this);
51269         view.on("beforerowremoved", this.clearSelections, this);
51270         view.on("beforerowsinserted", this.clearSelections, this);
51271         if(this.grid.isEditor){
51272             this.grid.on("beforeedit", this.beforeEdit,  this);
51273         }
51274     },
51275
51276         //private
51277     beforeEdit : function(e){
51278         this.select(e.row, e.column, false, true, e.record);
51279     },
51280
51281         //private
51282     onRowUpdated : function(v, index, r){
51283         if(this.selection && this.selection.record == r){
51284             v.onCellSelect(index, this.selection.cell[1]);
51285         }
51286     },
51287
51288         //private
51289     onViewChange : function(){
51290         this.clearSelections(true);
51291     },
51292
51293         /**
51294          * Returns the currently selected cell,.
51295          * @return {Array} The selected cell (row, column) or null if none selected.
51296          */
51297     getSelectedCell : function(){
51298         return this.selection ? this.selection.cell : null;
51299     },
51300
51301     /**
51302      * Clears all selections.
51303      * @param {Boolean} true to prevent the gridview from being notified about the change.
51304      */
51305     clearSelections : function(preventNotify){
51306         var s = this.selection;
51307         if(s){
51308             if(preventNotify !== true){
51309                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51310             }
51311             this.selection = null;
51312             this.fireEvent("selectionchange", this, null);
51313         }
51314     },
51315
51316     /**
51317      * Returns true if there is a selection.
51318      * @return {Boolean}
51319      */
51320     hasSelection : function(){
51321         return this.selection ? true : false;
51322     },
51323
51324     /** @ignore */
51325     handleMouseDown : function(e, t){
51326         var v = this.grid.getView();
51327         if(this.isLocked()){
51328             return;
51329         };
51330         var row = v.findRowIndex(t);
51331         var cell = v.findCellIndex(t);
51332         if(row !== false && cell !== false){
51333             this.select(row, cell);
51334         }
51335     },
51336
51337     /**
51338      * Selects a cell.
51339      * @param {Number} rowIndex
51340      * @param {Number} collIndex
51341      */
51342     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51343         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51344             this.clearSelections();
51345             r = r || this.grid.dataSource.getAt(rowIndex);
51346             this.selection = {
51347                 record : r,
51348                 cell : [rowIndex, colIndex]
51349             };
51350             if(!preventViewNotify){
51351                 var v = this.grid.getView();
51352                 v.onCellSelect(rowIndex, colIndex);
51353                 if(preventFocus !== true){
51354                     v.focusCell(rowIndex, colIndex);
51355                 }
51356             }
51357             this.fireEvent("cellselect", this, rowIndex, colIndex);
51358             this.fireEvent("selectionchange", this, this.selection);
51359         }
51360     },
51361
51362         //private
51363     isSelectable : function(rowIndex, colIndex, cm){
51364         return !cm.isHidden(colIndex);
51365     },
51366
51367     /** @ignore */
51368     handleKeyDown : function(e){
51369         //Roo.log('Cell Sel Model handleKeyDown');
51370         if(!e.isNavKeyPress()){
51371             return;
51372         }
51373         var g = this.grid, s = this.selection;
51374         if(!s){
51375             e.stopEvent();
51376             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51377             if(cell){
51378                 this.select(cell[0], cell[1]);
51379             }
51380             return;
51381         }
51382         var sm = this;
51383         var walk = function(row, col, step){
51384             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51385         };
51386         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51387         var newCell;
51388
51389       
51390
51391         switch(k){
51392             case e.TAB:
51393                 // handled by onEditorKey
51394                 if (g.isEditor && g.editing) {
51395                     return;
51396                 }
51397                 if(e.shiftKey) {
51398                     newCell = walk(r, c-1, -1);
51399                 } else {
51400                     newCell = walk(r, c+1, 1);
51401                 }
51402                 break;
51403             
51404             case e.DOWN:
51405                newCell = walk(r+1, c, 1);
51406                 break;
51407             
51408             case e.UP:
51409                 newCell = walk(r-1, c, -1);
51410                 break;
51411             
51412             case e.RIGHT:
51413                 newCell = walk(r, c+1, 1);
51414                 break;
51415             
51416             case e.LEFT:
51417                 newCell = walk(r, c-1, -1);
51418                 break;
51419             
51420             case e.ENTER:
51421                 
51422                 if(g.isEditor && !g.editing){
51423                    g.startEditing(r, c);
51424                    e.stopEvent();
51425                    return;
51426                 }
51427                 
51428                 
51429              break;
51430         };
51431         if(newCell){
51432             this.select(newCell[0], newCell[1]);
51433             e.stopEvent();
51434             
51435         }
51436     },
51437
51438     acceptsNav : function(row, col, cm){
51439         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51440     },
51441     /**
51442      * Selects a cell.
51443      * @param {Number} field (not used) - as it's normally used as a listener
51444      * @param {Number} e - event - fake it by using
51445      *
51446      * var e = Roo.EventObjectImpl.prototype;
51447      * e.keyCode = e.TAB
51448      *
51449      * 
51450      */
51451     onEditorKey : function(field, e){
51452         
51453         var k = e.getKey(),
51454             newCell,
51455             g = this.grid,
51456             ed = g.activeEditor,
51457             forward = false;
51458         ///Roo.log('onEditorKey' + k);
51459         
51460         
51461         if (this.enter_is_tab && k == e.ENTER) {
51462             k = e.TAB;
51463         }
51464         
51465         if(k == e.TAB){
51466             if(e.shiftKey){
51467                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51468             }else{
51469                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51470                 forward = true;
51471             }
51472             
51473             e.stopEvent();
51474             
51475         }else if(k == e.ENTER &&  !e.ctrlKey){
51476             ed.completeEdit();
51477             e.stopEvent();
51478             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51479         }else if(k == e.ESC){
51480             ed.cancelEdit();
51481         }
51482         
51483         
51484         if(newCell){
51485             //Roo.log('next cell after edit');
51486             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51487         } else if (forward) {
51488             // tabbed past last
51489             this.fireEvent.defer(100, this, ['tabend',this]);
51490         }
51491     }
51492 });/*
51493  * Based on:
51494  * Ext JS Library 1.1.1
51495  * Copyright(c) 2006-2007, Ext JS, LLC.
51496  *
51497  * Originally Released Under LGPL - original licence link has changed is not relivant.
51498  *
51499  * Fork - LGPL
51500  * <script type="text/javascript">
51501  */
51502  
51503 /**
51504  * @class Roo.grid.EditorGrid
51505  * @extends Roo.grid.Grid
51506  * Class for creating and editable grid.
51507  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51508  * The container MUST have some type of size defined for the grid to fill. The container will be 
51509  * automatically set to position relative if it isn't already.
51510  * @param {Object} dataSource The data model to bind to
51511  * @param {Object} colModel The column model with info about this grid's columns
51512  */
51513 Roo.grid.EditorGrid = function(container, config){
51514     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51515     this.getGridEl().addClass("xedit-grid");
51516
51517     if(!this.selModel){
51518         this.selModel = new Roo.grid.CellSelectionModel();
51519     }
51520
51521     this.activeEditor = null;
51522
51523         this.addEvents({
51524             /**
51525              * @event beforeedit
51526              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51527              * <ul style="padding:5px;padding-left:16px;">
51528              * <li>grid - This grid</li>
51529              * <li>record - The record being edited</li>
51530              * <li>field - The field name being edited</li>
51531              * <li>value - The value for the field being edited.</li>
51532              * <li>row - The grid row index</li>
51533              * <li>column - The grid column index</li>
51534              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51535              * </ul>
51536              * @param {Object} e An edit event (see above for description)
51537              */
51538             "beforeedit" : true,
51539             /**
51540              * @event afteredit
51541              * Fires after a cell is edited. <br />
51542              * <ul style="padding:5px;padding-left:16px;">
51543              * <li>grid - This grid</li>
51544              * <li>record - The record being edited</li>
51545              * <li>field - The field name being edited</li>
51546              * <li>value - The value being set</li>
51547              * <li>originalValue - The original value for the field, before the edit.</li>
51548              * <li>row - The grid row index</li>
51549              * <li>column - The grid column index</li>
51550              * </ul>
51551              * @param {Object} e An edit event (see above for description)
51552              */
51553             "afteredit" : true,
51554             /**
51555              * @event validateedit
51556              * Fires after a cell is edited, but before the value is set in the record. 
51557          * You can use this to modify the value being set in the field, Return false
51558              * to cancel the change. The edit event object has the following properties <br />
51559              * <ul style="padding:5px;padding-left:16px;">
51560          * <li>editor - This editor</li>
51561              * <li>grid - This grid</li>
51562              * <li>record - The record being edited</li>
51563              * <li>field - The field name being edited</li>
51564              * <li>value - The value being set</li>
51565              * <li>originalValue - The original value for the field, before the edit.</li>
51566              * <li>row - The grid row index</li>
51567              * <li>column - The grid column index</li>
51568              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51569              * </ul>
51570              * @param {Object} e An edit event (see above for description)
51571              */
51572             "validateedit" : true
51573         });
51574     this.on("bodyscroll", this.stopEditing,  this);
51575     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51576 };
51577
51578 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51579     /**
51580      * @cfg {Number} clicksToEdit
51581      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51582      */
51583     clicksToEdit: 2,
51584
51585     // private
51586     isEditor : true,
51587     // private
51588     trackMouseOver: false, // causes very odd FF errors
51589
51590     onCellDblClick : function(g, row, col){
51591         this.startEditing(row, col);
51592     },
51593
51594     onEditComplete : function(ed, value, startValue){
51595         this.editing = false;
51596         this.activeEditor = null;
51597         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51598         var r = ed.record;
51599         var field = this.colModel.getDataIndex(ed.col);
51600         var e = {
51601             grid: this,
51602             record: r,
51603             field: field,
51604             originalValue: startValue,
51605             value: value,
51606             row: ed.row,
51607             column: ed.col,
51608             cancel:false,
51609             editor: ed
51610         };
51611         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51612         cell.show();
51613           
51614         if(String(value) !== String(startValue)){
51615             
51616             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51617                 r.set(field, e.value);
51618                 // if we are dealing with a combo box..
51619                 // then we also set the 'name' colum to be the displayField
51620                 if (ed.field.displayField && ed.field.name) {
51621                     r.set(ed.field.name, ed.field.el.dom.value);
51622                 }
51623                 
51624                 delete e.cancel; //?? why!!!
51625                 this.fireEvent("afteredit", e);
51626             }
51627         } else {
51628             this.fireEvent("afteredit", e); // always fire it!
51629         }
51630         this.view.focusCell(ed.row, ed.col);
51631     },
51632
51633     /**
51634      * Starts editing the specified for the specified row/column
51635      * @param {Number} rowIndex
51636      * @param {Number} colIndex
51637      */
51638     startEditing : function(row, col){
51639         this.stopEditing();
51640         if(this.colModel.isCellEditable(col, row)){
51641             this.view.ensureVisible(row, col, true);
51642           
51643             var r = this.dataSource.getAt(row);
51644             var field = this.colModel.getDataIndex(col);
51645             var cell = Roo.get(this.view.getCell(row,col));
51646             var e = {
51647                 grid: this,
51648                 record: r,
51649                 field: field,
51650                 value: r.data[field],
51651                 row: row,
51652                 column: col,
51653                 cancel:false 
51654             };
51655             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51656                 this.editing = true;
51657                 var ed = this.colModel.getCellEditor(col, row);
51658                 
51659                 if (!ed) {
51660                     return;
51661                 }
51662                 if(!ed.rendered){
51663                     ed.render(ed.parentEl || document.body);
51664                 }
51665                 ed.field.reset();
51666                
51667                 cell.hide();
51668                 
51669                 (function(){ // complex but required for focus issues in safari, ie and opera
51670                     ed.row = row;
51671                     ed.col = col;
51672                     ed.record = r;
51673                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51674                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51675                     this.activeEditor = ed;
51676                     var v = r.data[field];
51677                     ed.startEdit(this.view.getCell(row, col), v);
51678                     // combo's with 'displayField and name set
51679                     if (ed.field.displayField && ed.field.name) {
51680                         ed.field.el.dom.value = r.data[ed.field.name];
51681                     }
51682                     
51683                     
51684                 }).defer(50, this);
51685             }
51686         }
51687     },
51688         
51689     /**
51690      * Stops any active editing
51691      */
51692     stopEditing : function(){
51693         if(this.activeEditor){
51694             this.activeEditor.completeEdit();
51695         }
51696         this.activeEditor = null;
51697     }
51698 });/*
51699  * Based on:
51700  * Ext JS Library 1.1.1
51701  * Copyright(c) 2006-2007, Ext JS, LLC.
51702  *
51703  * Originally Released Under LGPL - original licence link has changed is not relivant.
51704  *
51705  * Fork - LGPL
51706  * <script type="text/javascript">
51707  */
51708
51709 // private - not really -- you end up using it !
51710 // This is a support class used internally by the Grid components
51711
51712 /**
51713  * @class Roo.grid.GridEditor
51714  * @extends Roo.Editor
51715  * Class for creating and editable grid elements.
51716  * @param {Object} config any settings (must include field)
51717  */
51718 Roo.grid.GridEditor = function(field, config){
51719     if (!config && field.field) {
51720         config = field;
51721         field = Roo.factory(config.field, Roo.form);
51722     }
51723     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51724     field.monitorTab = false;
51725 };
51726
51727 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51728     
51729     /**
51730      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51731      */
51732     
51733     alignment: "tl-tl",
51734     autoSize: "width",
51735     hideEl : false,
51736     cls: "x-small-editor x-grid-editor",
51737     shim:false,
51738     shadow:"frame"
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   
51752 Roo.grid.PropertyRecord = Roo.data.Record.create([
51753     {name:'name',type:'string'},  'value'
51754 ]);
51755
51756
51757 Roo.grid.PropertyStore = function(grid, source){
51758     this.grid = grid;
51759     this.store = new Roo.data.Store({
51760         recordType : Roo.grid.PropertyRecord
51761     });
51762     this.store.on('update', this.onUpdate,  this);
51763     if(source){
51764         this.setSource(source);
51765     }
51766     Roo.grid.PropertyStore.superclass.constructor.call(this);
51767 };
51768
51769
51770
51771 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51772     setSource : function(o){
51773         this.source = o;
51774         this.store.removeAll();
51775         var data = [];
51776         for(var k in o){
51777             if(this.isEditableValue(o[k])){
51778                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51779             }
51780         }
51781         this.store.loadRecords({records: data}, {}, true);
51782     },
51783
51784     onUpdate : function(ds, record, type){
51785         if(type == Roo.data.Record.EDIT){
51786             var v = record.data['value'];
51787             var oldValue = record.modified['value'];
51788             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51789                 this.source[record.id] = v;
51790                 record.commit();
51791                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51792             }else{
51793                 record.reject();
51794             }
51795         }
51796     },
51797
51798     getProperty : function(row){
51799        return this.store.getAt(row);
51800     },
51801
51802     isEditableValue: function(val){
51803         if(val && val instanceof Date){
51804             return true;
51805         }else if(typeof val == 'object' || typeof val == 'function'){
51806             return false;
51807         }
51808         return true;
51809     },
51810
51811     setValue : function(prop, value){
51812         this.source[prop] = value;
51813         this.store.getById(prop).set('value', value);
51814     },
51815
51816     getSource : function(){
51817         return this.source;
51818     }
51819 });
51820
51821 Roo.grid.PropertyColumnModel = function(grid, store){
51822     this.grid = grid;
51823     var g = Roo.grid;
51824     g.PropertyColumnModel.superclass.constructor.call(this, [
51825         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51826         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51827     ]);
51828     this.store = store;
51829     this.bselect = Roo.DomHelper.append(document.body, {
51830         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51831             {tag: 'option', value: 'true', html: 'true'},
51832             {tag: 'option', value: 'false', html: 'false'}
51833         ]
51834     });
51835     Roo.id(this.bselect);
51836     var f = Roo.form;
51837     this.editors = {
51838         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51839         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51840         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51841         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51842         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51843     };
51844     this.renderCellDelegate = this.renderCell.createDelegate(this);
51845     this.renderPropDelegate = this.renderProp.createDelegate(this);
51846 };
51847
51848 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51849     
51850     
51851     nameText : 'Name',
51852     valueText : 'Value',
51853     
51854     dateFormat : 'm/j/Y',
51855     
51856     
51857     renderDate : function(dateVal){
51858         return dateVal.dateFormat(this.dateFormat);
51859     },
51860
51861     renderBool : function(bVal){
51862         return bVal ? 'true' : 'false';
51863     },
51864
51865     isCellEditable : function(colIndex, rowIndex){
51866         return colIndex == 1;
51867     },
51868
51869     getRenderer : function(col){
51870         return col == 1 ?
51871             this.renderCellDelegate : this.renderPropDelegate;
51872     },
51873
51874     renderProp : function(v){
51875         return this.getPropertyName(v);
51876     },
51877
51878     renderCell : function(val){
51879         var rv = val;
51880         if(val instanceof Date){
51881             rv = this.renderDate(val);
51882         }else if(typeof val == 'boolean'){
51883             rv = this.renderBool(val);
51884         }
51885         return Roo.util.Format.htmlEncode(rv);
51886     },
51887
51888     getPropertyName : function(name){
51889         var pn = this.grid.propertyNames;
51890         return pn && pn[name] ? pn[name] : name;
51891     },
51892
51893     getCellEditor : function(colIndex, rowIndex){
51894         var p = this.store.getProperty(rowIndex);
51895         var n = p.data['name'], val = p.data['value'];
51896         
51897         if(typeof(this.grid.customEditors[n]) == 'string'){
51898             return this.editors[this.grid.customEditors[n]];
51899         }
51900         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51901             return this.grid.customEditors[n];
51902         }
51903         if(val instanceof Date){
51904             return this.editors['date'];
51905         }else if(typeof val == 'number'){
51906             return this.editors['number'];
51907         }else if(typeof val == 'boolean'){
51908             return this.editors['boolean'];
51909         }else{
51910             return this.editors['string'];
51911         }
51912     }
51913 });
51914
51915 /**
51916  * @class Roo.grid.PropertyGrid
51917  * @extends Roo.grid.EditorGrid
51918  * This class represents the  interface of a component based property grid control.
51919  * <br><br>Usage:<pre><code>
51920  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51921       
51922  });
51923  // set any options
51924  grid.render();
51925  * </code></pre>
51926   
51927  * @constructor
51928  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51929  * The container MUST have some type of size defined for the grid to fill. The container will be
51930  * automatically set to position relative if it isn't already.
51931  * @param {Object} config A config object that sets properties on this grid.
51932  */
51933 Roo.grid.PropertyGrid = function(container, config){
51934     config = config || {};
51935     var store = new Roo.grid.PropertyStore(this);
51936     this.store = store;
51937     var cm = new Roo.grid.PropertyColumnModel(this, store);
51938     store.store.sort('name', 'ASC');
51939     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51940         ds: store.store,
51941         cm: cm,
51942         enableColLock:false,
51943         enableColumnMove:false,
51944         stripeRows:false,
51945         trackMouseOver: false,
51946         clicksToEdit:1
51947     }, config));
51948     this.getGridEl().addClass('x-props-grid');
51949     this.lastEditRow = null;
51950     this.on('columnresize', this.onColumnResize, this);
51951     this.addEvents({
51952          /**
51953              * @event beforepropertychange
51954              * Fires before a property changes (return false to stop?)
51955              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51956              * @param {String} id Record Id
51957              * @param {String} newval New Value
51958          * @param {String} oldval Old Value
51959              */
51960         "beforepropertychange": true,
51961         /**
51962              * @event propertychange
51963              * Fires after a property changes
51964              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51965              * @param {String} id Record Id
51966              * @param {String} newval New Value
51967          * @param {String} oldval Old Value
51968              */
51969         "propertychange": true
51970     });
51971     this.customEditors = this.customEditors || {};
51972 };
51973 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51974     
51975      /**
51976      * @cfg {Object} customEditors map of colnames=> custom editors.
51977      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51978      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51979      * false disables editing of the field.
51980          */
51981     
51982       /**
51983      * @cfg {Object} propertyNames map of property Names to their displayed value
51984          */
51985     
51986     render : function(){
51987         Roo.grid.PropertyGrid.superclass.render.call(this);
51988         this.autoSize.defer(100, this);
51989     },
51990
51991     autoSize : function(){
51992         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51993         if(this.view){
51994             this.view.fitColumns();
51995         }
51996     },
51997
51998     onColumnResize : function(){
51999         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52000         this.autoSize();
52001     },
52002     /**
52003      * Sets the data for the Grid
52004      * accepts a Key => Value object of all the elements avaiable.
52005      * @param {Object} data  to appear in grid.
52006      */
52007     setSource : function(source){
52008         this.store.setSource(source);
52009         //this.autoSize();
52010     },
52011     /**
52012      * Gets all the data from the grid.
52013      * @return {Object} data  data stored in grid
52014      */
52015     getSource : function(){
52016         return this.store.getSource();
52017     }
52018 });/*
52019  * Based on:
52020  * Ext JS Library 1.1.1
52021  * Copyright(c) 2006-2007, Ext JS, LLC.
52022  *
52023  * Originally Released Under LGPL - original licence link has changed is not relivant.
52024  *
52025  * Fork - LGPL
52026  * <script type="text/javascript">
52027  */
52028  
52029 /**
52030  * @class Roo.LoadMask
52031  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52032  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52033  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52034  * element's UpdateManager load indicator and will be destroyed after the initial load.
52035  * @constructor
52036  * Create a new LoadMask
52037  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52038  * @param {Object} config The config object
52039  */
52040 Roo.LoadMask = function(el, config){
52041     this.el = Roo.get(el);
52042     Roo.apply(this, config);
52043     if(this.store){
52044         this.store.on('beforeload', this.onBeforeLoad, this);
52045         this.store.on('load', this.onLoad, this);
52046         this.store.on('loadexception', this.onLoadException, this);
52047         this.removeMask = false;
52048     }else{
52049         var um = this.el.getUpdateManager();
52050         um.showLoadIndicator = false; // disable the default indicator
52051         um.on('beforeupdate', this.onBeforeLoad, this);
52052         um.on('update', this.onLoad, this);
52053         um.on('failure', this.onLoad, this);
52054         this.removeMask = true;
52055     }
52056 };
52057
52058 Roo.LoadMask.prototype = {
52059     /**
52060      * @cfg {Boolean} removeMask
52061      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52062      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52063      */
52064     /**
52065      * @cfg {String} msg
52066      * The text to display in a centered loading message box (defaults to 'Loading...')
52067      */
52068     msg : 'Loading...',
52069     /**
52070      * @cfg {String} msgCls
52071      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52072      */
52073     msgCls : 'x-mask-loading',
52074
52075     /**
52076      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52077      * @type Boolean
52078      */
52079     disabled: false,
52080
52081     /**
52082      * Disables the mask to prevent it from being displayed
52083      */
52084     disable : function(){
52085        this.disabled = true;
52086     },
52087
52088     /**
52089      * Enables the mask so that it can be displayed
52090      */
52091     enable : function(){
52092         this.disabled = false;
52093     },
52094     
52095     onLoadException : function()
52096     {
52097         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52098             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52099         }
52100         this.el.unmask(this.removeMask);
52101     },
52102     // private
52103     onLoad : function()
52104     {
52105         this.el.unmask(this.removeMask);
52106     },
52107
52108     // private
52109     onBeforeLoad : function(){
52110         if(!this.disabled){
52111             this.el.mask(this.msg, this.msgCls);
52112         }
52113     },
52114
52115     // private
52116     destroy : function(){
52117         if(this.store){
52118             this.store.un('beforeload', this.onBeforeLoad, this);
52119             this.store.un('load', this.onLoad, this);
52120             this.store.un('loadexception', this.onLoadException, this);
52121         }else{
52122             var um = this.el.getUpdateManager();
52123             um.un('beforeupdate', this.onBeforeLoad, this);
52124             um.un('update', this.onLoad, this);
52125             um.un('failure', this.onLoad, this);
52126         }
52127     }
52128 };/*
52129  * Based on:
52130  * Ext JS Library 1.1.1
52131  * Copyright(c) 2006-2007, Ext JS, LLC.
52132  *
52133  * Originally Released Under LGPL - original licence link has changed is not relivant.
52134  *
52135  * Fork - LGPL
52136  * <script type="text/javascript">
52137  */
52138 Roo.XTemplate = function(){
52139     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52140     var s = this.html;
52141
52142     s = ['<tpl>', s, '</tpl>'].join('');
52143
52144     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52145
52146     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52147     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52148     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52149     var m, id = 0;
52150     var tpls = [];
52151
52152     while(m = s.match(re)){
52153        var m2 = m[0].match(nameRe);
52154        var m3 = m[0].match(ifRe);
52155        var m4 = m[0].match(execRe);
52156        var exp = null, fn = null, exec = null;
52157        var name = m2 && m2[1] ? m2[1] : '';
52158        if(m3){
52159            exp = m3 && m3[1] ? m3[1] : null;
52160            if(exp){
52161                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52162            }
52163        }
52164        if(m4){
52165            exp = m4 && m4[1] ? m4[1] : null;
52166            if(exp){
52167                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52168            }
52169        }
52170        if(name){
52171            switch(name){
52172                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52173                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52174                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52175            }
52176        }
52177        tpls.push({
52178             id: id,
52179             target: name,
52180             exec: exec,
52181             test: fn,
52182             body: m[1]||''
52183         });
52184        s = s.replace(m[0], '{xtpl'+ id + '}');
52185        ++id;
52186     }
52187     for(var i = tpls.length-1; i >= 0; --i){
52188         this.compileTpl(tpls[i]);
52189     }
52190     this.master = tpls[tpls.length-1];
52191     this.tpls = tpls;
52192 };
52193 Roo.extend(Roo.XTemplate, Roo.Template, {
52194
52195     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52196
52197     applySubTemplate : function(id, values, parent){
52198         var t = this.tpls[id];
52199         if(t.test && !t.test.call(this, values, parent)){
52200             return '';
52201         }
52202         if(t.exec && t.exec.call(this, values, parent)){
52203             return '';
52204         }
52205         var vs = t.target ? t.target.call(this, values, parent) : values;
52206         parent = t.target ? values : parent;
52207         if(t.target && vs instanceof Array){
52208             var buf = [];
52209             for(var i = 0, len = vs.length; i < len; i++){
52210                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52211             }
52212             return buf.join('');
52213         }
52214         return t.compiled.call(this, vs, parent);
52215     },
52216
52217     compileTpl : function(tpl){
52218         var fm = Roo.util.Format;
52219         var useF = this.disableFormats !== true;
52220         var sep = Roo.isGecko ? "+" : ",";
52221         var fn = function(m, name, format, args){
52222             if(name.substr(0, 4) == 'xtpl'){
52223                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52224             }
52225             var v;
52226             if(name.indexOf('.') != -1){
52227                 v = name;
52228             }else{
52229                 v = "values['" + name + "']";
52230             }
52231             if(format && useF){
52232                 args = args ? ',' + args : "";
52233                 if(format.substr(0, 5) != "this."){
52234                     format = "fm." + format + '(';
52235                 }else{
52236                     format = 'this.call("'+ format.substr(5) + '", ';
52237                     args = ", values";
52238                 }
52239             }else{
52240                 args= ''; format = "("+v+" === undefined ? '' : ";
52241             }
52242             return "'"+ sep + format + v + args + ")"+sep+"'";
52243         };
52244         var body;
52245         // branched to use + in gecko and [].join() in others
52246         if(Roo.isGecko){
52247             body = "tpl.compiled = function(values, parent){ return '" +
52248                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52249                     "';};";
52250         }else{
52251             body = ["tpl.compiled = function(values, parent){ return ['"];
52252             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52253             body.push("'].join('');};");
52254             body = body.join('');
52255         }
52256         /** eval:var:zzzzzzz */
52257         eval(body);
52258         return this;
52259     },
52260
52261     applyTemplate : function(values){
52262         return this.master.compiled.call(this, values, {});
52263         var s = this.subs;
52264     },
52265
52266     apply : function(){
52267         return this.applyTemplate.apply(this, arguments);
52268     },
52269
52270     compile : function(){return this;}
52271 });
52272
52273 Roo.XTemplate.from = function(el){
52274     el = Roo.getDom(el);
52275     return new Roo.XTemplate(el.value || el.innerHTML);
52276 };/*
52277  * Original code for Roojs - LGPL
52278  * <script type="text/javascript">
52279  */
52280  
52281 /**
52282  * @class Roo.XComponent
52283  * A delayed Element creator...
52284  * Or a way to group chunks of interface together.
52285  * 
52286  * Mypart.xyx = new Roo.XComponent({
52287
52288     parent : 'Mypart.xyz', // empty == document.element.!!
52289     order : '001',
52290     name : 'xxxx'
52291     region : 'xxxx'
52292     disabled : function() {} 
52293      
52294     tree : function() { // return an tree of xtype declared components
52295         var MODULE = this;
52296         return 
52297         {
52298             xtype : 'NestedLayoutPanel',
52299             // technicall
52300         }
52301      ]
52302  *})
52303  *
52304  *
52305  * It can be used to build a big heiracy, with parent etc.
52306  * or you can just use this to render a single compoent to a dom element
52307  * MYPART.render(Roo.Element | String(id) | dom_element )
52308  * 
52309  * @extends Roo.util.Observable
52310  * @constructor
52311  * @param cfg {Object} configuration of component
52312  * 
52313  */
52314 Roo.XComponent = function(cfg) {
52315     Roo.apply(this, cfg);
52316     this.addEvents({ 
52317         /**
52318              * @event built
52319              * Fires when this the componnt is built
52320              * @param {Roo.XComponent} c the component
52321              */
52322         'built' : true,
52323         /**
52324              * @event buildcomplete
52325              * Fires on the top level element when all elements have been built
52326              * @param {Roo.XComponent} c the top level component.
52327          */
52328         'buildcomplete' : true
52329         
52330     });
52331     this.region = this.region || 'center'; // default..
52332     Roo.XComponent.register(this);
52333     this.modules = false;
52334     this.el = false; // where the layout goes..
52335     
52336     
52337 }
52338 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52339     /**
52340      * @property el
52341      * The created element (with Roo.factory())
52342      * @type {Roo.Layout}
52343      */
52344     el  : false,
52345     
52346     /**
52347      * @property el
52348      * for BC  - use el in new code
52349      * @type {Roo.Layout}
52350      */
52351     panel : false,
52352     
52353     /**
52354      * @property layout
52355      * for BC  - use el in new code
52356      * @type {Roo.Layout}
52357      */
52358     layout : false,
52359     
52360      /**
52361      * @cfg {Function|boolean} disabled
52362      * If this module is disabled by some rule, return true from the funtion
52363      */
52364     disabled : false,
52365     
52366     /**
52367      * @cfg {String} parent 
52368      * Name of parent element which it get xtype added to..
52369      */
52370     parent: false,
52371     
52372     /**
52373      * @cfg {String} order
52374      * Used to set the order in which elements are created (usefull for multiple tabs)
52375      */
52376     
52377     order : false,
52378     /**
52379      * @cfg {String} name
52380      * String to display while loading.
52381      */
52382     name : false,
52383     /**
52384      * @cfg {String} region
52385      * Region to render component to (defaults to center)
52386      */
52387     region : 'center',
52388     
52389     /**
52390      * @cfg {Array} items
52391      * A single item array - the first element is the root of the tree..
52392      * It's done this way to stay compatible with the Xtype system...
52393      */
52394     items : false,
52395     
52396     
52397      /**
52398      * render
52399      * render element to dom or tree
52400      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52401      */
52402     
52403     render : function(el)
52404     {
52405         
52406         el = el || false;
52407         var hp = this.parent ? 1 : 0;
52408         
52409         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52410             // if parent is a '#.....' string, then let's use that..
52411             var ename = this.parent.substr(1)
52412             this.parent = false;
52413             el = Roo.get(ename);
52414             if (!el) {
52415                 Roo.log("Warning - element can not be found :#" + ename );
52416                 return;
52417             }
52418         }
52419         
52420         
52421         if (!this.parent) {
52422             
52423             el = el ? Roo.get(el) : false;
52424             
52425             // it's a top level one..
52426             this.parent =  {
52427                 el : new Roo.BorderLayout(el || document.body, {
52428                 
52429                      center: {
52430                          titlebar: false,
52431                          autoScroll:false,
52432                          closeOnTab: true,
52433                          tabPosition: 'top',
52434                           //resizeTabs: true,
52435                          alwaysShowTabs: el && hp? false :  true,
52436                          hideTabs: el || !hp ? true :  false,
52437                          minTabWidth: 140
52438                      }
52439                  })
52440             }
52441         }
52442         
52443         
52444             
52445         var tree = this.tree();
52446         tree.region = tree.region || this.region;
52447         this.el = this.parent.el.addxtype(tree);
52448         this.fireEvent('built', this);
52449         
52450         this.panel = this.el;
52451         this.layout = this.panel.layout;    
52452          
52453     }
52454     
52455 });
52456
52457 Roo.apply(Roo.XComponent, {
52458     
52459     /**
52460      * @property  buildCompleted
52461      * True when the builder has completed building the interface.
52462      * @type Boolean
52463      */
52464     buildCompleted : false,
52465      
52466     /**
52467      * @property  topModule
52468      * the upper most module - uses document.element as it's constructor.
52469      * @type Object
52470      */
52471      
52472     topModule  : false,
52473       
52474     /**
52475      * @property  modules
52476      * array of modules to be created by registration system.
52477      * @type {Array} of Roo.XComponent
52478      */
52479     
52480     modules : [],
52481     /**
52482      * @property  elmodules
52483      * array of modules to be created by which use #ID 
52484      * @type {Array} of Roo.XComponent
52485      */
52486      
52487     elmodules : [],
52488
52489     
52490     /**
52491      * Register components to be built later.
52492      *
52493      * This solves the following issues
52494      * - Building is not done on page load, but after an authentication process has occured.
52495      * - Interface elements are registered on page load
52496      * - Parent Interface elements may not be loaded before child, so this handles that..
52497      * 
52498      *
52499      * example:
52500      * 
52501      * MyApp.register({
52502           order : '000001',
52503           module : 'Pman.Tab.projectMgr',
52504           region : 'center',
52505           parent : 'Pman.layout',
52506           disabled : false,  // or use a function..
52507         })
52508      
52509      * * @param {Object} details about module
52510      */
52511     register : function(obj) {
52512         this.modules.push(obj);
52513          
52514     },
52515     /**
52516      * convert a string to an object..
52517      * eg. 'AAA.BBB' -> finds AAA.BBB
52518
52519      */
52520     
52521     toObject : function(str)
52522     {
52523         if (!str || typeof(str) == 'object') {
52524             return str;
52525         }
52526         if (str.substring(0,1) == '#') {
52527             return str;
52528         }
52529
52530         var ar = str.split('.');
52531         var rt, o;
52532         rt = ar.shift();
52533             /** eval:var:o */
52534         try {
52535             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52536         } catch (e) {
52537             throw "Module not found : " + str;
52538         }
52539         
52540         if (o === false) {
52541             throw "Module not found : " + str;
52542         }
52543         Roo.each(ar, function(e) {
52544             if (typeof(o[e]) == 'undefined') {
52545                 throw "Module not found : " + str;
52546             }
52547             o = o[e];
52548         });
52549         
52550         return o;
52551         
52552     },
52553     
52554     
52555     /**
52556      * move modules into their correct place in the tree..
52557      * 
52558      */
52559     preBuild : function ()
52560     {
52561         var _t = this;
52562         Roo.each(this.modules , function (obj)
52563         {
52564             var opar = obj.parent;
52565             try { 
52566                 obj.parent = this.toObject(opar);
52567             } catch(e) {
52568                 Roo.log(e.toString());
52569                 return;
52570             }
52571             
52572             if (!obj.parent) {
52573                 this.topModule = obj;
52574                 return;
52575             }
52576             if (typeof(obj.parent) == 'string') {
52577                 this.elmodules.push(obj);
52578                 return;
52579             }
52580             if (obj.parent.constructor != Roo.XComponent) {
52581                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52582             }
52583             if (!obj.parent.modules) {
52584                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52585                     function(o) { return o.order + '' }
52586                 );
52587             }
52588             
52589             obj.parent.modules.add(obj);
52590         }, this);
52591     },
52592     
52593      /**
52594      * make a list of modules to build.
52595      * @return {Array} list of modules. 
52596      */ 
52597     
52598     buildOrder : function()
52599     {
52600         var _this = this;
52601         var cmp = function(a,b) {   
52602             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52603         };
52604         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52605             throw "No top level modules to build";
52606         }
52607         
52608         // make a flat list in order of modules to build.
52609         var mods = this.topModule ? [ this.topModule ] : [];
52610         Roo.each(this.elmodules,function(e) { mods.push(e) });
52611
52612         
52613         // add modules to their parents..
52614         var addMod = function(m) {
52615            // Roo.debug && Roo.log(m.modKey);
52616             
52617             mods.push(m);
52618             if (m.modules) {
52619                 m.modules.keySort('ASC',  cmp );
52620                 m.modules.each(addMod);
52621             }
52622             // not sure if this is used any more..
52623             if (m.finalize) {
52624                 m.finalize.name = m.name + " (clean up) ";
52625                 mods.push(m.finalize);
52626             }
52627             
52628         }
52629         if (this.topModule) { 
52630             this.topModule.modules.keySort('ASC',  cmp );
52631             this.topModule.modules.each(addMod);
52632         }
52633         return mods;
52634     },
52635     
52636      /**
52637      * Build the registered modules.
52638      * @param {Object} parent element.
52639      * @param {Function} optional method to call after module has been added.
52640      * 
52641      */ 
52642    
52643     build : function() 
52644     {
52645         
52646         this.preBuild();
52647         var mods = this.buildOrder();
52648       
52649         //this.allmods = mods;
52650         //Roo.debug && Roo.log(mods);
52651         //return;
52652         if (!mods.length) { // should not happen
52653             throw "NO modules!!!";
52654         }
52655         
52656         
52657         
52658         // flash it up as modal - so we store the mask!?
52659         Roo.MessageBox.show({ title: 'loading' });
52660         Roo.MessageBox.show({
52661            title: "Please wait...",
52662            msg: "Building Interface...",
52663            width:450,
52664            progress:true,
52665            closable:false,
52666            modal: false
52667           
52668         });
52669         var total = mods.length;
52670         
52671         var _this = this;
52672         var progressRun = function() {
52673             if (!mods.length) {
52674                 Roo.debug && Roo.log('hide?');
52675                 Roo.MessageBox.hide();
52676                 if (_this.topModule) { 
52677                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52678                 }
52679                 // THE END...
52680                 return false;   
52681             }
52682             
52683             var m = mods.shift();
52684             
52685             
52686             Roo.debug && Roo.log(m);
52687             // not sure if this is supported any more.. - modules that are are just function
52688             if (typeof(m) == 'function') { 
52689                 m.call(this);
52690                 return progressRun.defer(10, _this);
52691             } 
52692             
52693             
52694             
52695             Roo.MessageBox.updateProgress(
52696                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52697                     " of " + total + 
52698                     (m.name ? (' - ' + m.name) : '')
52699                     );
52700             
52701          
52702             // is the module disabled?
52703             var disabled = (typeof(m.disabled) == 'function') ?
52704                 m.disabled.call(m.module.disabled) : m.disabled;    
52705             
52706             
52707             if (disabled) {
52708                 return progressRun(); // we do not update the display!
52709             }
52710             
52711             // now build 
52712             
52713             m.render();
52714             // it's 10 on top level, and 1 on others??? why...
52715             return progressRun.defer(10, _this);
52716              
52717         }
52718         progressRun.defer(1, _this);
52719      
52720         
52721         
52722     }
52723     
52724      
52725    
52726     
52727     
52728 });
52729  //<script type="text/javascript">
52730
52731
52732 /**
52733  * @class Roo.Login
52734  * @extends Roo.LayoutDialog
52735  * A generic Login Dialog..... - only one needed in theory!?!?
52736  *
52737  * Fires XComponent builder on success...
52738  * 
52739  * Sends 
52740  *    username,password, lang = for login actions.
52741  *    check = 1 for periodic checking that sesion is valid.
52742  *    passwordRequest = email request password
52743  *    logout = 1 = to logout
52744  * 
52745  * Affects: (this id="????" elements)
52746  *   loading  (removed) (used to indicate application is loading)
52747  *   loading-mask (hides) (used to hide application when it's building loading)
52748  *   
52749  * 
52750  * Usage: 
52751  *    
52752  * 
52753  * Myapp.login = Roo.Login({
52754      url: xxxx,
52755    
52756      realm : 'Myapp', 
52757      
52758      
52759      method : 'POST',
52760      
52761      
52762      * 
52763  })
52764  * 
52765  * 
52766  * 
52767  **/
52768  
52769 Roo.Login = function(cfg)
52770 {
52771     this.addEvents({
52772         'refreshed' : true
52773     });
52774     
52775     Roo.apply(this,cfg);
52776     
52777     Roo.onReady(function() {
52778         this.onLoad();
52779     }, this);
52780     // call parent..
52781     
52782    
52783     Roo.Login.superclass.constructor.call(this, this);
52784     //this.addxtype(this.items[0]);
52785     
52786     
52787 }
52788
52789
52790 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52791     
52792     /**
52793      * @cfg {String} method
52794      * Method used to query for login details.
52795      */
52796     
52797     method : 'POST',
52798     /**
52799      * @cfg {String} url
52800      * URL to query login data. - eg. baseURL + '/Login.php'
52801      */
52802     url : '',
52803     
52804     /**
52805      * @property user
52806      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52807      * @type {Object} 
52808      */
52809     user : false,
52810     /**
52811      * @property checkFails
52812      * Number of times we have attempted to get authentication check, and failed.
52813      * @type {Number} 
52814      */
52815     checkFails : 0,
52816       /**
52817      * @property intervalID
52818      * The window interval that does the constant login checking.
52819      * @type {Number} 
52820      */
52821     intervalID : 0,
52822     
52823     
52824     onLoad : function() // called on page load...
52825     {
52826         // load 
52827          
52828         if (Roo.get('loading')) { // clear any loading indicator..
52829             Roo.get('loading').remove();
52830         }
52831         
52832         //this.switchLang('en'); // set the language to english..
52833        
52834         this.check({
52835             success:  function(response, opts)  {  // check successfull...
52836             
52837                 var res = this.processResponse(response);
52838                 this.checkFails =0;
52839                 if (!res.success) { // error!
52840                     this.checkFails = 5;
52841                     //console.log('call failure');
52842                     return this.failure(response,opts);
52843                 }
52844                 
52845                 if (!res.data.id) { // id=0 == login failure.
52846                     return this.show();
52847                 }
52848                 
52849                               
52850                         //console.log(success);
52851                 this.fillAuth(res.data);   
52852                 this.checkFails =0;
52853                 Roo.XComponent.build();
52854             },
52855             failure : this.show
52856         });
52857         
52858     }, 
52859     
52860     
52861     check: function(cfg) // called every so often to refresh cookie etc..
52862     {
52863         if (cfg.again) { // could be undefined..
52864             this.checkFails++;
52865         } else {
52866             this.checkFails = 0;
52867         }
52868         var _this = this;
52869         if (this.sending) {
52870             if ( this.checkFails > 4) {
52871                 Roo.MessageBox.alert("Error",  
52872                     "Error getting authentication status. - try reloading, or wait a while", function() {
52873                         _this.sending = false;
52874                     }); 
52875                 return;
52876             }
52877             cfg.again = true;
52878             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52879             return;
52880         }
52881         this.sending = true;
52882         
52883         Roo.Ajax.request({  
52884             url: this.url,
52885             params: {
52886                 getAuthUser: true
52887             },  
52888             method: this.method,
52889             success:  cfg.success || this.success,
52890             failure : cfg.failure || this.failure,
52891             scope : this,
52892             callCfg : cfg
52893               
52894         });  
52895     }, 
52896     
52897     
52898     logout: function()
52899     {
52900         window.onbeforeunload = function() { }; // false does not work for IE..
52901         this.user = false;
52902         var _this = this;
52903         
52904         Roo.Ajax.request({  
52905             url: this.url,
52906             params: {
52907                 logout: 1
52908             },  
52909             method: 'GET',
52910             failure : function() {
52911                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52912                     document.location = document.location.toString() + '?ts=' + Math.random();
52913                 });
52914                 
52915             },
52916             success : function() {
52917                 _this.user = false;
52918                 this.checkFails =0;
52919                 // fixme..
52920                 document.location = document.location.toString() + '?ts=' + Math.random();
52921             }
52922               
52923               
52924         }); 
52925     },
52926     
52927     processResponse : function (response)
52928     {
52929         var res = '';
52930         try {
52931             res = Roo.decode(response.responseText);
52932             // oops...
52933             if (typeof(res) != 'object') {
52934                 res = { success : false, errorMsg : res, errors : true };
52935             }
52936             if (typeof(res.success) == 'undefined') {
52937                 res.success = false;
52938             }
52939             
52940         } catch(e) {
52941             res = { success : false,  errorMsg : response.responseText, errors : true };
52942         }
52943         return res;
52944     },
52945     
52946     success : function(response, opts)  // check successfull...
52947     {  
52948         this.sending = false;
52949         var res = this.processResponse(response);
52950         if (!res.success) {
52951             return this.failure(response, opts);
52952         }
52953         if (!res.data || !res.data.id) {
52954             return this.failure(response,opts);
52955         }
52956         //console.log(res);
52957         this.fillAuth(res.data);
52958         
52959         this.checkFails =0;
52960         
52961     },
52962     
52963     
52964     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52965     {
52966         this.authUser = -1;
52967         this.sending = false;
52968         var res = this.processResponse(response);
52969         //console.log(res);
52970         if ( this.checkFails > 2) {
52971         
52972             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52973                 "Error getting authentication status. - try reloading"); 
52974             return;
52975         }
52976         opts.callCfg.again = true;
52977         this.check.defer(1000, this, [ opts.callCfg ]);
52978         return;  
52979     },
52980     
52981     
52982     
52983     fillAuth: function(au) {
52984         this.startAuthCheck();
52985         this.authUserId = au.id;
52986         this.authUser = au;
52987         this.lastChecked = new Date();
52988         this.fireEvent('refreshed', au);
52989         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52990         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52991         au.lang = au.lang || 'en';
52992         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52993         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52994         this.switchLang(au.lang );
52995         
52996      
52997         // open system... - -on setyp..
52998         if (this.authUserId  < 0) {
52999             Roo.MessageBox.alert("Warning", 
53000                 "This is an open system - please set up a admin user with a password.");  
53001         }
53002          
53003         //Pman.onload(); // which should do nothing if it's a re-auth result...
53004         
53005              
53006     },
53007     
53008     startAuthCheck : function() // starter for timeout checking..
53009     {
53010         if (this.intervalID) { // timer already in place...
53011             return false;
53012         }
53013         var _this = this;
53014         this.intervalID =  window.setInterval(function() {
53015               _this.check(false);
53016             }, 120000); // every 120 secs = 2mins..
53017         
53018         
53019     },
53020          
53021     
53022     switchLang : function (lang) 
53023     {
53024         _T = typeof(_T) == 'undefined' ? false : _T;
53025           if (!_T || !lang.length) {
53026             return;
53027         }
53028         
53029         if (!_T && lang != 'en') {
53030             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53031             return;
53032         }
53033         
53034         if (typeof(_T.en) == 'undefined') {
53035             _T.en = {};
53036             Roo.apply(_T.en, _T);
53037         }
53038         
53039         if (typeof(_T[lang]) == 'undefined') {
53040             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53041             return;
53042         }
53043         
53044         
53045         Roo.apply(_T, _T[lang]);
53046         // just need to set the text values for everything...
53047         var _this = this;
53048         /* this will not work ...
53049         if (this.form) { 
53050             
53051                
53052             function formLabel(name, val) {
53053                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53054             }
53055             
53056             formLabel('password', "Password"+':');
53057             formLabel('username', "Email Address"+':');
53058             formLabel('lang', "Language"+':');
53059             this.dialog.setTitle("Login");
53060             this.dialog.buttons[0].setText("Forgot Password");
53061             this.dialog.buttons[1].setText("Login");
53062         }
53063         */
53064         
53065         
53066     },
53067     
53068     
53069     title: "Login",
53070     modal: true,
53071     width:  350,
53072     //height: 230,
53073     height: 180,
53074     shadow: true,
53075     minWidth:200,
53076     minHeight:180,
53077     //proxyDrag: true,
53078     closable: false,
53079     draggable: false,
53080     collapsible: false,
53081     resizable: false,
53082     center: {  // needed??
53083         autoScroll:false,
53084         titlebar: false,
53085        // tabPosition: 'top',
53086         hideTabs: true,
53087         closeOnTab: true,
53088         alwaysShowTabs: false
53089     } ,
53090     listeners : {
53091         
53092         show  : function(dlg)
53093         {
53094             //console.log(this);
53095             this.form = this.layout.getRegion('center').activePanel.form;
53096             this.form.dialog = dlg;
53097             this.buttons[0].form = this.form;
53098             this.buttons[0].dialog = dlg;
53099             this.buttons[1].form = this.form;
53100             this.buttons[1].dialog = dlg;
53101            
53102            //this.resizeToLogo.defer(1000,this);
53103             // this is all related to resizing for logos..
53104             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53105            //// if (!sz) {
53106              //   this.resizeToLogo.defer(1000,this);
53107              //   return;
53108            // }
53109             //var w = Ext.lib.Dom.getViewWidth() - 100;
53110             //var h = Ext.lib.Dom.getViewHeight() - 100;
53111             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53112             //this.center();
53113             if (this.disabled) {
53114                 this.hide();
53115                 return;
53116             }
53117             
53118             if (this.user.id < 0) { // used for inital setup situations.
53119                 return;
53120             }
53121             
53122             if (this.intervalID) {
53123                 // remove the timer
53124                 window.clearInterval(this.intervalID);
53125                 this.intervalID = false;
53126             }
53127             
53128             
53129             if (Roo.get('loading')) {
53130                 Roo.get('loading').remove();
53131             }
53132             if (Roo.get('loading-mask')) {
53133                 Roo.get('loading-mask').hide();
53134             }
53135             
53136             //incomming._node = tnode;
53137             this.form.reset();
53138             //this.dialog.modal = !modal;
53139             //this.dialog.show();
53140             this.el.unmask(); 
53141             
53142             
53143             this.form.setValues({
53144                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53145                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53146             });
53147             
53148             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53149             if (this.form.findField('username').getValue().length > 0 ){
53150                 this.form.findField('password').focus();
53151             } else {
53152                this.form.findField('username').focus();
53153             }
53154     
53155         }
53156     },
53157     items : [
53158          {
53159        
53160             xtype : 'ContentPanel',
53161             xns : Roo,
53162             region: 'center',
53163             fitToFrame : true,
53164             
53165             items : [
53166     
53167                 {
53168                
53169                     xtype : 'Form',
53170                     xns : Roo.form,
53171                     labelWidth: 100,
53172                     style : 'margin: 10px;',
53173                     
53174                     listeners : {
53175                         actionfailed : function(f, act) {
53176                             // form can return { errors: .... }
53177                                 
53178                             //act.result.errors // invalid form element list...
53179                             //act.result.errorMsg// invalid form element list...
53180                             
53181                             this.dialog.el.unmask();
53182                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53183                                         "Login failed - communication error - try again.");
53184                                       
53185                         },
53186                         actioncomplete: function(re, act) {
53187                              
53188                             Roo.state.Manager.set(
53189                                 this.dialog.realm + '.username',  
53190                                     this.findField('username').getValue()
53191                             );
53192                             Roo.state.Manager.set(
53193                                 this.dialog.realm + '.lang',  
53194                                 this.findField('lang').getValue() 
53195                             );
53196                             
53197                             this.dialog.fillAuth(act.result.data);
53198                               
53199                             this.dialog.hide();
53200                             
53201                             if (Roo.get('loading-mask')) {
53202                                 Roo.get('loading-mask').show();
53203                             }
53204                             Roo.XComponent.build();
53205                             
53206                              
53207                             
53208                         }
53209                     },
53210                     items : [
53211                         {
53212                             xtype : 'TextField',
53213                             xns : Roo.form,
53214                             fieldLabel: "Email Address",
53215                             name: 'username',
53216                             width:200,
53217                             autoCreate : {tag: "input", type: "text", size: "20"}
53218                         },
53219                         {
53220                             xtype : 'TextField',
53221                             xns : Roo.form,
53222                             fieldLabel: "Password",
53223                             inputType: 'password',
53224                             name: 'password',
53225                             width:200,
53226                             autoCreate : {tag: "input", type: "text", size: "20"},
53227                             listeners : {
53228                                 specialkey : function(e,ev) {
53229                                     if (ev.keyCode == 13) {
53230                                         this.form.dialog.el.mask("Logging in");
53231                                         this.form.doAction('submit', {
53232                                             url: this.form.dialog.url,
53233                                             method: this.form.dialog.method
53234                                         });
53235                                     }
53236                                 }
53237                             }  
53238                         },
53239                         {
53240                             xtype : 'ComboBox',
53241                             xns : Roo.form,
53242                             fieldLabel: "Language",
53243                             name : 'langdisp',
53244                             store: {
53245                                 xtype : 'SimpleStore',
53246                                 fields: ['lang', 'ldisp'],
53247                                 data : [
53248                                     [ 'en', 'English' ],
53249                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53250                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53251                                 ]
53252                             },
53253                             
53254                             valueField : 'lang',
53255                             hiddenName:  'lang',
53256                             width: 200,
53257                             displayField:'ldisp',
53258                             typeAhead: false,
53259                             editable: false,
53260                             mode: 'local',
53261                             triggerAction: 'all',
53262                             emptyText:'Select a Language...',
53263                             selectOnFocus:true,
53264                             listeners : {
53265                                 select :  function(cb, rec, ix) {
53266                                     this.form.switchLang(rec.data.lang);
53267                                 }
53268                             }
53269                         
53270                         }
53271                     ]
53272                 }
53273                   
53274                 
53275             ]
53276         }
53277     ],
53278     buttons : [
53279         {
53280             xtype : 'Button',
53281             xns : 'Roo',
53282             text : "Forgot Password",
53283             listeners : {
53284                 click : function() {
53285                     //console.log(this);
53286                     var n = this.form.findField('username').getValue();
53287                     if (!n.length) {
53288                         Roo.MessageBox.alert("Error", "Fill in your email address");
53289                         return;
53290                     }
53291                     Roo.Ajax.request({
53292                         url: this.dialog.url,
53293                         params: {
53294                             passwordRequest: n
53295                         },
53296                         method: this.dialog.method,
53297                         success:  function(response, opts)  {  // check successfull...
53298                         
53299                             var res = this.dialog.processResponse(response);
53300                             if (!res.success) { // error!
53301                                Roo.MessageBox.alert("Error" ,
53302                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53303                                return;
53304                             }
53305                             Roo.MessageBox.alert("Notice" ,
53306                                 "Please check you email for the Password Reset message");
53307                         },
53308                         failure : function() {
53309                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53310                         }
53311                         
53312                     });
53313                 }
53314             }
53315         },
53316         {
53317             xtype : 'Button',
53318             xns : 'Roo',
53319             text : "Login",
53320             listeners : {
53321                 
53322                 click : function () {
53323                         
53324                     this.dialog.el.mask("Logging in");
53325                     this.form.doAction('submit', {
53326                             url: this.dialog.url,
53327                             method: this.dialog.method
53328                     });
53329                 }
53330             }
53331         }
53332     ]
53333   
53334   
53335 })
53336  
53337
53338
53339