Roo/form/ComboBoxArray.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 /**
1778  * @class Roo.lib.Dom
1779  * @static
1780  * 
1781  * Dom utils (from YIU afaik)
1782  * 
1783  **/
1784 Roo.lib.Dom = {
1785     /**
1786      * Get the view width
1787      * @param {Boolean} full True will get the full document, otherwise it's the view width
1788      * @return {Number} The width
1789      */
1790      
1791     getViewWidth : function(full) {
1792         return full ? this.getDocumentWidth() : this.getViewportWidth();
1793     },
1794     /**
1795      * Get the view height
1796      * @param {Boolean} full True will get the full document, otherwise it's the view height
1797      * @return {Number} The height
1798      */
1799     getViewHeight : function(full) {
1800         return full ? this.getDocumentHeight() : this.getViewportHeight();
1801     },
1802
1803     getDocumentHeight: function() {
1804         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1805         return Math.max(scrollHeight, this.getViewportHeight());
1806     },
1807
1808     getDocumentWidth: function() {
1809         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1810         return Math.max(scrollWidth, this.getViewportWidth());
1811     },
1812
1813     getViewportHeight: function() {
1814         var height = self.innerHeight;
1815         var mode = document.compatMode;
1816
1817         if ((mode || Roo.isIE) && !Roo.isOpera) {
1818             height = (mode == "CSS1Compat") ?
1819                      document.documentElement.clientHeight :
1820                      document.body.clientHeight;
1821         }
1822
1823         return height;
1824     },
1825
1826     getViewportWidth: function() {
1827         var width = self.innerWidth;
1828         var mode = document.compatMode;
1829
1830         if (mode || Roo.isIE) {
1831             width = (mode == "CSS1Compat") ?
1832                     document.documentElement.clientWidth :
1833                     document.body.clientWidth;
1834         }
1835         return width;
1836     },
1837
1838     isAncestor : function(p, c) {
1839         p = Roo.getDom(p);
1840         c = Roo.getDom(c);
1841         if (!p || !c) {
1842             return false;
1843         }
1844
1845         if (p.contains && !Roo.isSafari) {
1846             return p.contains(c);
1847         } else if (p.compareDocumentPosition) {
1848             return !!(p.compareDocumentPosition(c) & 16);
1849         } else {
1850             var parent = c.parentNode;
1851             while (parent) {
1852                 if (parent == p) {
1853                     return true;
1854                 }
1855                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1856                     return false;
1857                 }
1858                 parent = parent.parentNode;
1859             }
1860             return false;
1861         }
1862     },
1863
1864     getRegion : function(el) {
1865         return Roo.lib.Region.getRegion(el);
1866     },
1867
1868     getY : function(el) {
1869         return this.getXY(el)[1];
1870     },
1871
1872     getX : function(el) {
1873         return this.getXY(el)[0];
1874     },
1875
1876     getXY : function(el) {
1877         var p, pe, b, scroll, bd = document.body;
1878         el = Roo.getDom(el);
1879         var fly = Roo.lib.AnimBase.fly;
1880         if (el.getBoundingClientRect) {
1881             b = el.getBoundingClientRect();
1882             scroll = fly(document).getScroll();
1883             return [b.left + scroll.left, b.top + scroll.top];
1884         }
1885         var x = 0, y = 0;
1886
1887         p = el;
1888
1889         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1890
1891         while (p) {
1892
1893             x += p.offsetLeft;
1894             y += p.offsetTop;
1895
1896             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1897                 hasAbsolute = true;
1898             }
1899
1900             if (Roo.isGecko) {
1901                 pe = fly(p);
1902
1903                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1904                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1905
1906
1907                 x += bl;
1908                 y += bt;
1909
1910
1911                 if (p != el && pe.getStyle('overflow') != 'visible') {
1912                     x += bl;
1913                     y += bt;
1914                 }
1915             }
1916             p = p.offsetParent;
1917         }
1918
1919         if (Roo.isSafari && hasAbsolute) {
1920             x -= bd.offsetLeft;
1921             y -= bd.offsetTop;
1922         }
1923
1924         if (Roo.isGecko && !hasAbsolute) {
1925             var dbd = fly(bd);
1926             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1927             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1928         }
1929
1930         p = el.parentNode;
1931         while (p && p != bd) {
1932             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1933                 x -= p.scrollLeft;
1934                 y -= p.scrollTop;
1935             }
1936             p = p.parentNode;
1937         }
1938         return [x, y];
1939     },
1940  
1941   
1942
1943
1944     setXY : function(el, xy) {
1945         el = Roo.fly(el, '_setXY');
1946         el.position();
1947         var pts = el.translatePoints(xy);
1948         if (xy[0] !== false) {
1949             el.dom.style.left = pts.left + "px";
1950         }
1951         if (xy[1] !== false) {
1952             el.dom.style.top = pts.top + "px";
1953         }
1954     },
1955
1956     setX : function(el, x) {
1957         this.setXY(el, [x, false]);
1958     },
1959
1960     setY : function(el, y) {
1961         this.setXY(el, [false, y]);
1962     }
1963 };
1964 /*
1965  * Portions of this file are based on pieces of Yahoo User Interface Library
1966  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1967  * YUI licensed under the BSD License:
1968  * http://developer.yahoo.net/yui/license.txt
1969  * <script type="text/javascript">
1970  *
1971  */
1972
1973 Roo.lib.Event = function() {
1974     var loadComplete = false;
1975     var listeners = [];
1976     var unloadListeners = [];
1977     var retryCount = 0;
1978     var onAvailStack = [];
1979     var counter = 0;
1980     var lastError = null;
1981
1982     return {
1983         POLL_RETRYS: 200,
1984         POLL_INTERVAL: 20,
1985         EL: 0,
1986         TYPE: 1,
1987         FN: 2,
1988         WFN: 3,
1989         OBJ: 3,
1990         ADJ_SCOPE: 4,
1991         _interval: null,
1992
1993         startInterval: function() {
1994             if (!this._interval) {
1995                 var self = this;
1996                 var callback = function() {
1997                     self._tryPreloadAttach();
1998                 };
1999                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2000
2001             }
2002         },
2003
2004         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2005             onAvailStack.push({ id:         p_id,
2006                 fn:         p_fn,
2007                 obj:        p_obj,
2008                 override:   p_override,
2009                 checkReady: false    });
2010
2011             retryCount = this.POLL_RETRYS;
2012             this.startInterval();
2013         },
2014
2015
2016         addListener: function(el, eventName, fn) {
2017             el = Roo.getDom(el);
2018             if (!el || !fn) {
2019                 return false;
2020             }
2021
2022             if ("unload" == eventName) {
2023                 unloadListeners[unloadListeners.length] =
2024                 [el, eventName, fn];
2025                 return true;
2026             }
2027
2028             var wrappedFn = function(e) {
2029                 return fn(Roo.lib.Event.getEvent(e));
2030             };
2031
2032             var li = [el, eventName, fn, wrappedFn];
2033
2034             var index = listeners.length;
2035             listeners[index] = li;
2036
2037             this.doAdd(el, eventName, wrappedFn, false);
2038             return true;
2039
2040         },
2041
2042
2043         removeListener: function(el, eventName, fn) {
2044             var i, len;
2045
2046             el = Roo.getDom(el);
2047
2048             if(!fn) {
2049                 return this.purgeElement(el, false, eventName);
2050             }
2051
2052
2053             if ("unload" == eventName) {
2054
2055                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2056                     var li = unloadListeners[i];
2057                     if (li &&
2058                         li[0] == el &&
2059                         li[1] == eventName &&
2060                         li[2] == fn) {
2061                         unloadListeners.splice(i, 1);
2062                         return true;
2063                     }
2064                 }
2065
2066                 return false;
2067             }
2068
2069             var cacheItem = null;
2070
2071
2072             var index = arguments[3];
2073
2074             if ("undefined" == typeof index) {
2075                 index = this._getCacheIndex(el, eventName, fn);
2076             }
2077
2078             if (index >= 0) {
2079                 cacheItem = listeners[index];
2080             }
2081
2082             if (!el || !cacheItem) {
2083                 return false;
2084             }
2085
2086             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2087
2088             delete listeners[index][this.WFN];
2089             delete listeners[index][this.FN];
2090             listeners.splice(index, 1);
2091
2092             return true;
2093
2094         },
2095
2096
2097         getTarget: function(ev, resolveTextNode) {
2098             ev = ev.browserEvent || ev;
2099             var t = ev.target || ev.srcElement;
2100             return this.resolveTextNode(t);
2101         },
2102
2103
2104         resolveTextNode: function(node) {
2105             if (Roo.isSafari && node && 3 == node.nodeType) {
2106                 return node.parentNode;
2107             } else {
2108                 return node;
2109             }
2110         },
2111
2112
2113         getPageX: function(ev) {
2114             ev = ev.browserEvent || ev;
2115             var x = ev.pageX;
2116             if (!x && 0 !== x) {
2117                 x = ev.clientX || 0;
2118
2119                 if (Roo.isIE) {
2120                     x += this.getScroll()[1];
2121                 }
2122             }
2123
2124             return x;
2125         },
2126
2127
2128         getPageY: function(ev) {
2129             ev = ev.browserEvent || ev;
2130             var y = ev.pageY;
2131             if (!y && 0 !== y) {
2132                 y = ev.clientY || 0;
2133
2134                 if (Roo.isIE) {
2135                     y += this.getScroll()[0];
2136                 }
2137             }
2138
2139
2140             return y;
2141         },
2142
2143
2144         getXY: function(ev) {
2145             ev = ev.browserEvent || ev;
2146             return [this.getPageX(ev), this.getPageY(ev)];
2147         },
2148
2149
2150         getRelatedTarget: function(ev) {
2151             ev = ev.browserEvent || ev;
2152             var t = ev.relatedTarget;
2153             if (!t) {
2154                 if (ev.type == "mouseout") {
2155                     t = ev.toElement;
2156                 } else if (ev.type == "mouseover") {
2157                     t = ev.fromElement;
2158                 }
2159             }
2160
2161             return this.resolveTextNode(t);
2162         },
2163
2164
2165         getTime: function(ev) {
2166             ev = ev.browserEvent || ev;
2167             if (!ev.time) {
2168                 var t = new Date().getTime();
2169                 try {
2170                     ev.time = t;
2171                 } catch(ex) {
2172                     this.lastError = ex;
2173                     return t;
2174                 }
2175             }
2176
2177             return ev.time;
2178         },
2179
2180
2181         stopEvent: function(ev) {
2182             this.stopPropagation(ev);
2183             this.preventDefault(ev);
2184         },
2185
2186
2187         stopPropagation: function(ev) {
2188             ev = ev.browserEvent || ev;
2189             if (ev.stopPropagation) {
2190                 ev.stopPropagation();
2191             } else {
2192                 ev.cancelBubble = true;
2193             }
2194         },
2195
2196
2197         preventDefault: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             if(ev.preventDefault) {
2200                 ev.preventDefault();
2201             } else {
2202                 ev.returnValue = false;
2203             }
2204         },
2205
2206
2207         getEvent: function(e) {
2208             var ev = e || window.event;
2209             if (!ev) {
2210                 var c = this.getEvent.caller;
2211                 while (c) {
2212                     ev = c.arguments[0];
2213                     if (ev && Event == ev.constructor) {
2214                         break;
2215                     }
2216                     c = c.caller;
2217                 }
2218             }
2219             return ev;
2220         },
2221
2222
2223         getCharCode: function(ev) {
2224             ev = ev.browserEvent || ev;
2225             return ev.charCode || ev.keyCode || 0;
2226         },
2227
2228
2229         _getCacheIndex: function(el, eventName, fn) {
2230             for (var i = 0,len = listeners.length; i < len; ++i) {
2231                 var li = listeners[i];
2232                 if (li &&
2233                     li[this.FN] == fn &&
2234                     li[this.EL] == el &&
2235                     li[this.TYPE] == eventName) {
2236                     return i;
2237                 }
2238             }
2239
2240             return -1;
2241         },
2242
2243
2244         elCache: {},
2245
2246
2247         getEl: function(id) {
2248             return document.getElementById(id);
2249         },
2250
2251
2252         clearCache: function() {
2253         },
2254
2255
2256         _load: function(e) {
2257             loadComplete = true;
2258             var EU = Roo.lib.Event;
2259
2260
2261             if (Roo.isIE) {
2262                 EU.doRemove(window, "load", EU._load);
2263             }
2264         },
2265
2266
2267         _tryPreloadAttach: function() {
2268
2269             if (this.locked) {
2270                 return false;
2271             }
2272
2273             this.locked = true;
2274
2275
2276             var tryAgain = !loadComplete;
2277             if (!tryAgain) {
2278                 tryAgain = (retryCount > 0);
2279             }
2280
2281
2282             var notAvail = [];
2283             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2284                 var item = onAvailStack[i];
2285                 if (item) {
2286                     var el = this.getEl(item.id);
2287
2288                     if (el) {
2289                         if (!item.checkReady ||
2290                             loadComplete ||
2291                             el.nextSibling ||
2292                             (document && document.body)) {
2293
2294                             var scope = el;
2295                             if (item.override) {
2296                                 if (item.override === true) {
2297                                     scope = item.obj;
2298                                 } else {
2299                                     scope = item.override;
2300                                 }
2301                             }
2302                             item.fn.call(scope, item.obj);
2303                             onAvailStack[i] = null;
2304                         }
2305                     } else {
2306                         notAvail.push(item);
2307                     }
2308                 }
2309             }
2310
2311             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2312
2313             if (tryAgain) {
2314
2315                 this.startInterval();
2316             } else {
2317                 clearInterval(this._interval);
2318                 this._interval = null;
2319             }
2320
2321             this.locked = false;
2322
2323             return true;
2324
2325         },
2326
2327
2328         purgeElement: function(el, recurse, eventName) {
2329             var elListeners = this.getListeners(el, eventName);
2330             if (elListeners) {
2331                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2332                     var l = elListeners[i];
2333                     this.removeListener(el, l.type, l.fn);
2334                 }
2335             }
2336
2337             if (recurse && el && el.childNodes) {
2338                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2339                     this.purgeElement(el.childNodes[i], recurse, eventName);
2340                 }
2341             }
2342         },
2343
2344
2345         getListeners: function(el, eventName) {
2346             var results = [], searchLists;
2347             if (!eventName) {
2348                 searchLists = [listeners, unloadListeners];
2349             } else if (eventName == "unload") {
2350                 searchLists = [unloadListeners];
2351             } else {
2352                 searchLists = [listeners];
2353             }
2354
2355             for (var j = 0; j < searchLists.length; ++j) {
2356                 var searchList = searchLists[j];
2357                 if (searchList && searchList.length > 0) {
2358                     for (var i = 0,len = searchList.length; i < len; ++i) {
2359                         var l = searchList[i];
2360                         if (l && l[this.EL] === el &&
2361                             (!eventName || eventName === l[this.TYPE])) {
2362                             results.push({
2363                                 type:   l[this.TYPE],
2364                                 fn:     l[this.FN],
2365                                 obj:    l[this.OBJ],
2366                                 adjust: l[this.ADJ_SCOPE],
2367                                 index:  i
2368                             });
2369                         }
2370                     }
2371                 }
2372             }
2373
2374             return (results.length) ? results : null;
2375         },
2376
2377
2378         _unload: function(e) {
2379
2380             var EU = Roo.lib.Event, i, j, l, len, index;
2381
2382             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2383                 l = unloadListeners[i];
2384                 if (l) {
2385                     var scope = window;
2386                     if (l[EU.ADJ_SCOPE]) {
2387                         if (l[EU.ADJ_SCOPE] === true) {
2388                             scope = l[EU.OBJ];
2389                         } else {
2390                             scope = l[EU.ADJ_SCOPE];
2391                         }
2392                     }
2393                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2394                     unloadListeners[i] = null;
2395                     l = null;
2396                     scope = null;
2397                 }
2398             }
2399
2400             unloadListeners = null;
2401
2402             if (listeners && listeners.length > 0) {
2403                 j = listeners.length;
2404                 while (j) {
2405                     index = j - 1;
2406                     l = listeners[index];
2407                     if (l) {
2408                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2409                                 l[EU.FN], index);
2410                     }
2411                     j = j - 1;
2412                 }
2413                 l = null;
2414
2415                 EU.clearCache();
2416             }
2417
2418             EU.doRemove(window, "unload", EU._unload);
2419
2420         },
2421
2422
2423         getScroll: function() {
2424             var dd = document.documentElement, db = document.body;
2425             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2426                 return [dd.scrollTop, dd.scrollLeft];
2427             } else if (db) {
2428                 return [db.scrollTop, db.scrollLeft];
2429             } else {
2430                 return [0, 0];
2431             }
2432         },
2433
2434
2435         doAdd: function () {
2436             if (window.addEventListener) {
2437                 return function(el, eventName, fn, capture) {
2438                     el.addEventListener(eventName, fn, (capture));
2439                 };
2440             } else if (window.attachEvent) {
2441                 return function(el, eventName, fn, capture) {
2442                     el.attachEvent("on" + eventName, fn);
2443                 };
2444             } else {
2445                 return function() {
2446                 };
2447             }
2448         }(),
2449
2450
2451         doRemove: function() {
2452             if (window.removeEventListener) {
2453                 return function (el, eventName, fn, capture) {
2454                     el.removeEventListener(eventName, fn, (capture));
2455                 };
2456             } else if (window.detachEvent) {
2457                 return function (el, eventName, fn) {
2458                     el.detachEvent("on" + eventName, fn);
2459                 };
2460             } else {
2461                 return function() {
2462                 };
2463             }
2464         }()
2465     };
2466     
2467 }();
2468 (function() {     
2469    
2470     var E = Roo.lib.Event;
2471     E.on = E.addListener;
2472     E.un = E.removeListener;
2473
2474     if (document && document.body) {
2475         E._load();
2476     } else {
2477         E.doAdd(window, "load", E._load);
2478     }
2479     E.doAdd(window, "unload", E._unload);
2480     E._tryPreloadAttach();
2481 })();
2482
2483 /*
2484  * Portions of this file are based on pieces of Yahoo User Interface Library
2485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2486  * YUI licensed under the BSD License:
2487  * http://developer.yahoo.net/yui/license.txt
2488  * <script type="text/javascript">
2489  *
2490  */
2491
2492 (function() {
2493     /**
2494      * @class Roo.lib.Ajax
2495      *
2496      */
2497     Roo.lib.Ajax = {
2498         /**
2499          * @static 
2500          */
2501         request : function(method, uri, cb, data, options) {
2502             if(options){
2503                 var hs = options.headers;
2504                 if(hs){
2505                     for(var h in hs){
2506                         if(hs.hasOwnProperty(h)){
2507                             this.initHeader(h, hs[h], false);
2508                         }
2509                     }
2510                 }
2511                 if(options.xmlData){
2512                     this.initHeader('Content-Type', 'text/xml', false);
2513                     method = 'POST';
2514                     data = options.xmlData;
2515                 }
2516             }
2517
2518             return this.asyncRequest(method, uri, cb, data);
2519         },
2520
2521         serializeForm : function(form) {
2522             if(typeof form == 'string') {
2523                 form = (document.getElementById(form) || document.forms[form]);
2524             }
2525
2526             var el, name, val, disabled, data = '', hasSubmit = false;
2527             for (var i = 0; i < form.elements.length; i++) {
2528                 el = form.elements[i];
2529                 disabled = form.elements[i].disabled;
2530                 name = form.elements[i].name;
2531                 val = form.elements[i].value;
2532
2533                 if (!disabled && name){
2534                     switch (el.type)
2535                             {
2536                         case 'select-one':
2537                         case 'select-multiple':
2538                             for (var j = 0; j < el.options.length; j++) {
2539                                 if (el.options[j].selected) {
2540                                     if (Roo.isIE) {
2541                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2542                                     }
2543                                     else {
2544                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2545                                     }
2546                                 }
2547                             }
2548                             break;
2549                         case 'radio':
2550                         case 'checkbox':
2551                             if (el.checked) {
2552                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             }
2554                             break;
2555                         case 'file':
2556
2557                         case undefined:
2558
2559                         case 'reset':
2560
2561                         case 'button':
2562
2563                             break;
2564                         case 'submit':
2565                             if(hasSubmit == false) {
2566                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2567                                 hasSubmit = true;
2568                             }
2569                             break;
2570                         default:
2571                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2572                             break;
2573                     }
2574                 }
2575             }
2576             data = data.substr(0, data.length - 1);
2577             return data;
2578         },
2579
2580         headers:{},
2581
2582         hasHeaders:false,
2583
2584         useDefaultHeader:true,
2585
2586         defaultPostHeader:'application/x-www-form-urlencoded',
2587
2588         useDefaultXhrHeader:true,
2589
2590         defaultXhrHeader:'XMLHttpRequest',
2591
2592         hasDefaultHeaders:true,
2593
2594         defaultHeaders:{},
2595
2596         poll:{},
2597
2598         timeout:{},
2599
2600         pollInterval:50,
2601
2602         transactionId:0,
2603
2604         setProgId:function(id)
2605         {
2606             this.activeX.unshift(id);
2607         },
2608
2609         setDefaultPostHeader:function(b)
2610         {
2611             this.useDefaultHeader = b;
2612         },
2613
2614         setDefaultXhrHeader:function(b)
2615         {
2616             this.useDefaultXhrHeader = b;
2617         },
2618
2619         setPollingInterval:function(i)
2620         {
2621             if (typeof i == 'number' && isFinite(i)) {
2622                 this.pollInterval = i;
2623             }
2624         },
2625
2626         createXhrObject:function(transactionId)
2627         {
2628             var obj,http;
2629             try
2630             {
2631
2632                 http = new XMLHttpRequest();
2633
2634                 obj = { conn:http, tId:transactionId };
2635             }
2636             catch(e)
2637             {
2638                 for (var i = 0; i < this.activeX.length; ++i) {
2639                     try
2640                     {
2641
2642                         http = new ActiveXObject(this.activeX[i]);
2643
2644                         obj = { conn:http, tId:transactionId };
2645                         break;
2646                     }
2647                     catch(e) {
2648                     }
2649                 }
2650             }
2651             finally
2652             {
2653                 return obj;
2654             }
2655         },
2656
2657         getConnectionObject:function()
2658         {
2659             var o;
2660             var tId = this.transactionId;
2661
2662             try
2663             {
2664                 o = this.createXhrObject(tId);
2665                 if (o) {
2666                     this.transactionId++;
2667                 }
2668             }
2669             catch(e) {
2670             }
2671             finally
2672             {
2673                 return o;
2674             }
2675         },
2676
2677         asyncRequest:function(method, uri, callback, postData)
2678         {
2679             var o = this.getConnectionObject();
2680
2681             if (!o) {
2682                 return null;
2683             }
2684             else {
2685                 o.conn.open(method, uri, true);
2686
2687                 if (this.useDefaultXhrHeader) {
2688                     if (!this.defaultHeaders['X-Requested-With']) {
2689                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2690                     }
2691                 }
2692
2693                 if(postData && this.useDefaultHeader){
2694                     this.initHeader('Content-Type', this.defaultPostHeader);
2695                 }
2696
2697                  if (this.hasDefaultHeaders || this.hasHeaders) {
2698                     this.setHeader(o);
2699                 }
2700
2701                 this.handleReadyState(o, callback);
2702                 o.conn.send(postData || null);
2703
2704                 return o;
2705             }
2706         },
2707
2708         handleReadyState:function(o, callback)
2709         {
2710             var oConn = this;
2711
2712             if (callback && callback.timeout) {
2713                 this.timeout[o.tId] = window.setTimeout(function() {
2714                     oConn.abort(o, callback, true);
2715                 }, callback.timeout);
2716             }
2717
2718             this.poll[o.tId] = window.setInterval(
2719                     function() {
2720                         if (o.conn && o.conn.readyState == 4) {
2721                             window.clearInterval(oConn.poll[o.tId]);
2722                             delete oConn.poll[o.tId];
2723
2724                             if(callback && callback.timeout) {
2725                                 window.clearTimeout(oConn.timeout[o.tId]);
2726                                 delete oConn.timeout[o.tId];
2727                             }
2728
2729                             oConn.handleTransactionResponse(o, callback);
2730                         }
2731                     }
2732                     , this.pollInterval);
2733         },
2734
2735         handleTransactionResponse:function(o, callback, isAbort)
2736         {
2737
2738             if (!callback) {
2739                 this.releaseObject(o);
2740                 return;
2741             }
2742
2743             var httpStatus, responseObject;
2744
2745             try
2746             {
2747                 if (o.conn.status !== undefined && o.conn.status != 0) {
2748                     httpStatus = o.conn.status;
2749                 }
2750                 else {
2751                     httpStatus = 13030;
2752                 }
2753             }
2754             catch(e) {
2755
2756
2757                 httpStatus = 13030;
2758             }
2759
2760             if (httpStatus >= 200 && httpStatus < 300) {
2761                 responseObject = this.createResponseObject(o, callback.argument);
2762                 if (callback.success) {
2763                     if (!callback.scope) {
2764                         callback.success(responseObject);
2765                     }
2766                     else {
2767
2768
2769                         callback.success.apply(callback.scope, [responseObject]);
2770                     }
2771                 }
2772             }
2773             else {
2774                 switch (httpStatus) {
2775
2776                     case 12002:
2777                     case 12029:
2778                     case 12030:
2779                     case 12031:
2780                     case 12152:
2781                     case 13030:
2782                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2783                         if (callback.failure) {
2784                             if (!callback.scope) {
2785                                 callback.failure(responseObject);
2786                             }
2787                             else {
2788                                 callback.failure.apply(callback.scope, [responseObject]);
2789                             }
2790                         }
2791                         break;
2792                     default:
2793                         responseObject = this.createResponseObject(o, callback.argument);
2794                         if (callback.failure) {
2795                             if (!callback.scope) {
2796                                 callback.failure(responseObject);
2797                             }
2798                             else {
2799                                 callback.failure.apply(callback.scope, [responseObject]);
2800                             }
2801                         }
2802                 }
2803             }
2804
2805             this.releaseObject(o);
2806             responseObject = null;
2807         },
2808
2809         createResponseObject:function(o, callbackArg)
2810         {
2811             var obj = {};
2812             var headerObj = {};
2813
2814             try
2815             {
2816                 var headerStr = o.conn.getAllResponseHeaders();
2817                 var header = headerStr.split('\n');
2818                 for (var i = 0; i < header.length; i++) {
2819                     var delimitPos = header[i].indexOf(':');
2820                     if (delimitPos != -1) {
2821                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2822                     }
2823                 }
2824             }
2825             catch(e) {
2826             }
2827
2828             obj.tId = o.tId;
2829             obj.status = o.conn.status;
2830             obj.statusText = o.conn.statusText;
2831             obj.getResponseHeader = headerObj;
2832             obj.getAllResponseHeaders = headerStr;
2833             obj.responseText = o.conn.responseText;
2834             obj.responseXML = o.conn.responseXML;
2835
2836             if (typeof callbackArg !== undefined) {
2837                 obj.argument = callbackArg;
2838             }
2839
2840             return obj;
2841         },
2842
2843         createExceptionObject:function(tId, callbackArg, isAbort)
2844         {
2845             var COMM_CODE = 0;
2846             var COMM_ERROR = 'communication failure';
2847             var ABORT_CODE = -1;
2848             var ABORT_ERROR = 'transaction aborted';
2849
2850             var obj = {};
2851
2852             obj.tId = tId;
2853             if (isAbort) {
2854                 obj.status = ABORT_CODE;
2855                 obj.statusText = ABORT_ERROR;
2856             }
2857             else {
2858                 obj.status = COMM_CODE;
2859                 obj.statusText = COMM_ERROR;
2860             }
2861
2862             if (callbackArg) {
2863                 obj.argument = callbackArg;
2864             }
2865
2866             return obj;
2867         },
2868
2869         initHeader:function(label, value, isDefault)
2870         {
2871             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2872
2873             if (headerObj[label] === undefined) {
2874                 headerObj[label] = value;
2875             }
2876             else {
2877
2878
2879                 headerObj[label] = value + "," + headerObj[label];
2880             }
2881
2882             if (isDefault) {
2883                 this.hasDefaultHeaders = true;
2884             }
2885             else {
2886                 this.hasHeaders = true;
2887             }
2888         },
2889
2890
2891         setHeader:function(o)
2892         {
2893             if (this.hasDefaultHeaders) {
2894                 for (var prop in this.defaultHeaders) {
2895                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2896                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2897                     }
2898                 }
2899             }
2900
2901             if (this.hasHeaders) {
2902                 for (var prop in this.headers) {
2903                     if (this.headers.hasOwnProperty(prop)) {
2904                         o.conn.setRequestHeader(prop, this.headers[prop]);
2905                     }
2906                 }
2907                 this.headers = {};
2908                 this.hasHeaders = false;
2909             }
2910         },
2911
2912         resetDefaultHeaders:function() {
2913             delete this.defaultHeaders;
2914             this.defaultHeaders = {};
2915             this.hasDefaultHeaders = false;
2916         },
2917
2918         abort:function(o, callback, isTimeout)
2919         {
2920             if(this.isCallInProgress(o)) {
2921                 o.conn.abort();
2922                 window.clearInterval(this.poll[o.tId]);
2923                 delete this.poll[o.tId];
2924                 if (isTimeout) {
2925                     delete this.timeout[o.tId];
2926                 }
2927
2928                 this.handleTransactionResponse(o, callback, true);
2929
2930                 return true;
2931             }
2932             else {
2933                 return false;
2934             }
2935         },
2936
2937
2938         isCallInProgress:function(o)
2939         {
2940             if (o && o.conn) {
2941                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2942             }
2943             else {
2944
2945                 return false;
2946             }
2947         },
2948
2949
2950         releaseObject:function(o)
2951         {
2952
2953             o.conn = null;
2954
2955             o = null;
2956         },
2957
2958         activeX:[
2959         'MSXML2.XMLHTTP.3.0',
2960         'MSXML2.XMLHTTP',
2961         'Microsoft.XMLHTTP'
2962         ]
2963
2964
2965     };
2966 })();/*
2967  * Portions of this file are based on pieces of Yahoo User Interface Library
2968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2969  * YUI licensed under the BSD License:
2970  * http://developer.yahoo.net/yui/license.txt
2971  * <script type="text/javascript">
2972  *
2973  */
2974
2975 Roo.lib.Region = function(t, r, b, l) {
2976     this.top = t;
2977     this[1] = t;
2978     this.right = r;
2979     this.bottom = b;
2980     this.left = l;
2981     this[0] = l;
2982 };
2983
2984
2985 Roo.lib.Region.prototype = {
2986     contains : function(region) {
2987         return ( region.left >= this.left &&
2988                  region.right <= this.right &&
2989                  region.top >= this.top &&
2990                  region.bottom <= this.bottom    );
2991
2992     },
2993
2994     getArea : function() {
2995         return ( (this.bottom - this.top) * (this.right - this.left) );
2996     },
2997
2998     intersect : function(region) {
2999         var t = Math.max(this.top, region.top);
3000         var r = Math.min(this.right, region.right);
3001         var b = Math.min(this.bottom, region.bottom);
3002         var l = Math.max(this.left, region.left);
3003
3004         if (b >= t && r >= l) {
3005             return new Roo.lib.Region(t, r, b, l);
3006         } else {
3007             return null;
3008         }
3009     },
3010     union : function(region) {
3011         var t = Math.min(this.top, region.top);
3012         var r = Math.max(this.right, region.right);
3013         var b = Math.max(this.bottom, region.bottom);
3014         var l = Math.min(this.left, region.left);
3015
3016         return new Roo.lib.Region(t, r, b, l);
3017     },
3018
3019     adjust : function(t, l, b, r) {
3020         this.top += t;
3021         this.left += l;
3022         this.right += r;
3023         this.bottom += b;
3024         return this;
3025     }
3026 };
3027
3028 Roo.lib.Region.getRegion = function(el) {
3029     var p = Roo.lib.Dom.getXY(el);
3030
3031     var t = p[1];
3032     var r = p[0] + el.offsetWidth;
3033     var b = p[1] + el.offsetHeight;
3034     var l = p[0];
3035
3036     return new Roo.lib.Region(t, r, b, l);
3037 };
3038 /*
3039  * Portions of this file are based on pieces of Yahoo User Interface Library
3040  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3041  * YUI licensed under the BSD License:
3042  * http://developer.yahoo.net/yui/license.txt
3043  * <script type="text/javascript">
3044  *
3045  */
3046 //@@dep Roo.lib.Region
3047
3048
3049 Roo.lib.Point = function(x, y) {
3050     if (x instanceof Array) {
3051         y = x[1];
3052         x = x[0];
3053     }
3054     this.x = this.right = this.left = this[0] = x;
3055     this.y = this.top = this.bottom = this[1] = y;
3056 };
3057
3058 Roo.lib.Point.prototype = new Roo.lib.Region();
3059 /*
3060  * Portions of this file are based on pieces of Yahoo User Interface Library
3061  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3062  * YUI licensed under the BSD License:
3063  * http://developer.yahoo.net/yui/license.txt
3064  * <script type="text/javascript">
3065  *
3066  */
3067  
3068 (function() {   
3069
3070     Roo.lib.Anim = {
3071         scroll : function(el, args, duration, easing, cb, scope) {
3072             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3073         },
3074
3075         motion : function(el, args, duration, easing, cb, scope) {
3076             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3077         },
3078
3079         color : function(el, args, duration, easing, cb, scope) {
3080             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3081         },
3082
3083         run : function(el, args, duration, easing, cb, scope, type) {
3084             type = type || Roo.lib.AnimBase;
3085             if (typeof easing == "string") {
3086                 easing = Roo.lib.Easing[easing];
3087             }
3088             var anim = new type(el, args, duration, easing);
3089             anim.animateX(function() {
3090                 Roo.callback(cb, scope);
3091             });
3092             return anim;
3093         }
3094     };
3095 })();/*
3096  * Portions of this file are based on pieces of Yahoo User Interface Library
3097  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3098  * YUI licensed under the BSD License:
3099  * http://developer.yahoo.net/yui/license.txt
3100  * <script type="text/javascript">
3101  *
3102  */
3103
3104 (function() {    
3105     var libFlyweight;
3106     
3107     function fly(el) {
3108         if (!libFlyweight) {
3109             libFlyweight = new Roo.Element.Flyweight();
3110         }
3111         libFlyweight.dom = el;
3112         return libFlyweight;
3113     }
3114
3115     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3116     
3117    
3118     
3119     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3120         if (el) {
3121             this.init(el, attributes, duration, method);
3122         }
3123     };
3124
3125     Roo.lib.AnimBase.fly = fly;
3126     
3127     
3128     
3129     Roo.lib.AnimBase.prototype = {
3130
3131         toString: function() {
3132             var el = this.getEl();
3133             var id = el.id || el.tagName;
3134             return ("Anim " + id);
3135         },
3136
3137         patterns: {
3138             noNegatives:        /width|height|opacity|padding/i,
3139             offsetAttribute:  /^((width|height)|(top|left))$/,
3140             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3141             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3142         },
3143
3144
3145         doMethod: function(attr, start, end) {
3146             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3147         },
3148
3149
3150         setAttribute: function(attr, val, unit) {
3151             if (this.patterns.noNegatives.test(attr)) {
3152                 val = (val > 0) ? val : 0;
3153             }
3154
3155             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3156         },
3157
3158
3159         getAttribute: function(attr) {
3160             var el = this.getEl();
3161             var val = fly(el).getStyle(attr);
3162
3163             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3164                 return parseFloat(val);
3165             }
3166
3167             var a = this.patterns.offsetAttribute.exec(attr) || [];
3168             var pos = !!( a[3] );
3169             var box = !!( a[2] );
3170
3171
3172             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3173                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3174             } else {
3175                 val = 0;
3176             }
3177
3178             return val;
3179         },
3180
3181
3182         getDefaultUnit: function(attr) {
3183             if (this.patterns.defaultUnit.test(attr)) {
3184                 return 'px';
3185             }
3186
3187             return '';
3188         },
3189
3190         animateX : function(callback, scope) {
3191             var f = function() {
3192                 this.onComplete.removeListener(f);
3193                 if (typeof callback == "function") {
3194                     callback.call(scope || this, this);
3195                 }
3196             };
3197             this.onComplete.addListener(f, this);
3198             this.animate();
3199         },
3200
3201
3202         setRuntimeAttribute: function(attr) {
3203             var start;
3204             var end;
3205             var attributes = this.attributes;
3206
3207             this.runtimeAttributes[attr] = {};
3208
3209             var isset = function(prop) {
3210                 return (typeof prop !== 'undefined');
3211             };
3212
3213             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3214                 return false;
3215             }
3216
3217             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3218
3219
3220             if (isset(attributes[attr]['to'])) {
3221                 end = attributes[attr]['to'];
3222             } else if (isset(attributes[attr]['by'])) {
3223                 if (start.constructor == Array) {
3224                     end = [];
3225                     for (var i = 0, len = start.length; i < len; ++i) {
3226                         end[i] = start[i] + attributes[attr]['by'][i];
3227                     }
3228                 } else {
3229                     end = start + attributes[attr]['by'];
3230                 }
3231             }
3232
3233             this.runtimeAttributes[attr].start = start;
3234             this.runtimeAttributes[attr].end = end;
3235
3236
3237             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3238         },
3239
3240
3241         init: function(el, attributes, duration, method) {
3242
3243             var isAnimated = false;
3244
3245
3246             var startTime = null;
3247
3248
3249             var actualFrames = 0;
3250
3251
3252             el = Roo.getDom(el);
3253
3254
3255             this.attributes = attributes || {};
3256
3257
3258             this.duration = duration || 1;
3259
3260
3261             this.method = method || Roo.lib.Easing.easeNone;
3262
3263
3264             this.useSeconds = true;
3265
3266
3267             this.currentFrame = 0;
3268
3269
3270             this.totalFrames = Roo.lib.AnimMgr.fps;
3271
3272
3273             this.getEl = function() {
3274                 return el;
3275             };
3276
3277
3278             this.isAnimated = function() {
3279                 return isAnimated;
3280             };
3281
3282
3283             this.getStartTime = function() {
3284                 return startTime;
3285             };
3286
3287             this.runtimeAttributes = {};
3288
3289
3290             this.animate = function() {
3291                 if (this.isAnimated()) {
3292                     return false;
3293                 }
3294
3295                 this.currentFrame = 0;
3296
3297                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3298
3299                 Roo.lib.AnimMgr.registerElement(this);
3300             };
3301
3302
3303             this.stop = function(finish) {
3304                 if (finish) {
3305                     this.currentFrame = this.totalFrames;
3306                     this._onTween.fire();
3307                 }
3308                 Roo.lib.AnimMgr.stop(this);
3309             };
3310
3311             var onStart = function() {
3312                 this.onStart.fire();
3313
3314                 this.runtimeAttributes = {};
3315                 for (var attr in this.attributes) {
3316                     this.setRuntimeAttribute(attr);
3317                 }
3318
3319                 isAnimated = true;
3320                 actualFrames = 0;
3321                 startTime = new Date();
3322             };
3323
3324
3325             var onTween = function() {
3326                 var data = {
3327                     duration: new Date() - this.getStartTime(),
3328                     currentFrame: this.currentFrame
3329                 };
3330
3331                 data.toString = function() {
3332                     return (
3333                             'duration: ' + data.duration +
3334                             ', currentFrame: ' + data.currentFrame
3335                             );
3336                 };
3337
3338                 this.onTween.fire(data);
3339
3340                 var runtimeAttributes = this.runtimeAttributes;
3341
3342                 for (var attr in runtimeAttributes) {
3343                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3344                 }
3345
3346                 actualFrames += 1;
3347             };
3348
3349             var onComplete = function() {
3350                 var actual_duration = (new Date() - startTime) / 1000 ;
3351
3352                 var data = {
3353                     duration: actual_duration,
3354                     frames: actualFrames,
3355                     fps: actualFrames / actual_duration
3356                 };
3357
3358                 data.toString = function() {
3359                     return (
3360                             'duration: ' + data.duration +
3361                             ', frames: ' + data.frames +
3362                             ', fps: ' + data.fps
3363                             );
3364                 };
3365
3366                 isAnimated = false;
3367                 actualFrames = 0;
3368                 this.onComplete.fire(data);
3369             };
3370
3371
3372             this._onStart = new Roo.util.Event(this);
3373             this.onStart = new Roo.util.Event(this);
3374             this.onTween = new Roo.util.Event(this);
3375             this._onTween = new Roo.util.Event(this);
3376             this.onComplete = new Roo.util.Event(this);
3377             this._onComplete = new Roo.util.Event(this);
3378             this._onStart.addListener(onStart);
3379             this._onTween.addListener(onTween);
3380             this._onComplete.addListener(onComplete);
3381         }
3382     };
3383 })();
3384 /*
3385  * Portions of this file are based on pieces of Yahoo User Interface Library
3386  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3387  * YUI licensed under the BSD License:
3388  * http://developer.yahoo.net/yui/license.txt
3389  * <script type="text/javascript">
3390  *
3391  */
3392
3393 Roo.lib.AnimMgr = new function() {
3394
3395         var thread = null;
3396
3397
3398         var queue = [];
3399
3400
3401         var tweenCount = 0;
3402
3403
3404         this.fps = 1000;
3405
3406
3407         this.delay = 1;
3408
3409
3410         this.registerElement = function(tween) {
3411             queue[queue.length] = tween;
3412             tweenCount += 1;
3413             tween._onStart.fire();
3414             this.start();
3415         };
3416
3417
3418         this.unRegister = function(tween, index) {
3419             tween._onComplete.fire();
3420             index = index || getIndex(tween);
3421             if (index != -1) {
3422                 queue.splice(index, 1);
3423             }
3424
3425             tweenCount -= 1;
3426             if (tweenCount <= 0) {
3427                 this.stop();
3428             }
3429         };
3430
3431
3432         this.start = function() {
3433             if (thread === null) {
3434                 thread = setInterval(this.run, this.delay);
3435             }
3436         };
3437
3438
3439         this.stop = function(tween) {
3440             if (!tween) {
3441                 clearInterval(thread);
3442
3443                 for (var i = 0, len = queue.length; i < len; ++i) {
3444                     if (queue[0].isAnimated()) {
3445                         this.unRegister(queue[0], 0);
3446                     }
3447                 }
3448
3449                 queue = [];
3450                 thread = null;
3451                 tweenCount = 0;
3452             }
3453             else {
3454                 this.unRegister(tween);
3455             }
3456         };
3457
3458
3459         this.run = function() {
3460             for (var i = 0, len = queue.length; i < len; ++i) {
3461                 var tween = queue[i];
3462                 if (!tween || !tween.isAnimated()) {
3463                     continue;
3464                 }
3465
3466                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3467                 {
3468                     tween.currentFrame += 1;
3469
3470                     if (tween.useSeconds) {
3471                         correctFrame(tween);
3472                     }
3473                     tween._onTween.fire();
3474                 }
3475                 else {
3476                     Roo.lib.AnimMgr.stop(tween, i);
3477                 }
3478             }
3479         };
3480
3481         var getIndex = function(anim) {
3482             for (var i = 0, len = queue.length; i < len; ++i) {
3483                 if (queue[i] == anim) {
3484                     return i;
3485                 }
3486             }
3487             return -1;
3488         };
3489
3490
3491         var correctFrame = function(tween) {
3492             var frames = tween.totalFrames;
3493             var frame = tween.currentFrame;
3494             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3495             var elapsed = (new Date() - tween.getStartTime());
3496             var tweak = 0;
3497
3498             if (elapsed < tween.duration * 1000) {
3499                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3500             } else {
3501                 tweak = frames - (frame + 1);
3502             }
3503             if (tweak > 0 && isFinite(tweak)) {
3504                 if (tween.currentFrame + tweak >= frames) {
3505                     tweak = frames - (frame + 1);
3506                 }
3507
3508                 tween.currentFrame += tweak;
3509             }
3510         };
3511     };/*
3512  * Portions of this file are based on pieces of Yahoo User Interface Library
3513  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3514  * YUI licensed under the BSD License:
3515  * http://developer.yahoo.net/yui/license.txt
3516  * <script type="text/javascript">
3517  *
3518  */
3519 Roo.lib.Bezier = new function() {
3520
3521         this.getPosition = function(points, t) {
3522             var n = points.length;
3523             var tmp = [];
3524
3525             for (var i = 0; i < n; ++i) {
3526                 tmp[i] = [points[i][0], points[i][1]];
3527             }
3528
3529             for (var j = 1; j < n; ++j) {
3530                 for (i = 0; i < n - j; ++i) {
3531                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3532                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3533                 }
3534             }
3535
3536             return [ tmp[0][0], tmp[0][1] ];
3537
3538         };
3539     };/*
3540  * Portions of this file are based on pieces of Yahoo User Interface Library
3541  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3542  * YUI licensed under the BSD License:
3543  * http://developer.yahoo.net/yui/license.txt
3544  * <script type="text/javascript">
3545  *
3546  */
3547 (function() {
3548
3549     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3550         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3551     };
3552
3553     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3554
3555     var fly = Roo.lib.AnimBase.fly;
3556     var Y = Roo.lib;
3557     var superclass = Y.ColorAnim.superclass;
3558     var proto = Y.ColorAnim.prototype;
3559
3560     proto.toString = function() {
3561         var el = this.getEl();
3562         var id = el.id || el.tagName;
3563         return ("ColorAnim " + id);
3564     };
3565
3566     proto.patterns.color = /color$/i;
3567     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3568     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3569     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3570     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3571
3572
3573     proto.parseColor = function(s) {
3574         if (s.length == 3) {
3575             return s;
3576         }
3577
3578         var c = this.patterns.hex.exec(s);
3579         if (c && c.length == 4) {
3580             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3581         }
3582
3583         c = this.patterns.rgb.exec(s);
3584         if (c && c.length == 4) {
3585             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3586         }
3587
3588         c = this.patterns.hex3.exec(s);
3589         if (c && c.length == 4) {
3590             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3591         }
3592
3593         return null;
3594     };
3595     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3596     proto.getAttribute = function(attr) {
3597         var el = this.getEl();
3598         if (this.patterns.color.test(attr)) {
3599             var val = fly(el).getStyle(attr);
3600
3601             if (this.patterns.transparent.test(val)) {
3602                 var parent = el.parentNode;
3603                 val = fly(parent).getStyle(attr);
3604
3605                 while (parent && this.patterns.transparent.test(val)) {
3606                     parent = parent.parentNode;
3607                     val = fly(parent).getStyle(attr);
3608                     if (parent.tagName.toUpperCase() == 'HTML') {
3609                         val = '#fff';
3610                     }
3611                 }
3612             }
3613         } else {
3614             val = superclass.getAttribute.call(this, attr);
3615         }
3616
3617         return val;
3618     };
3619     proto.getAttribute = function(attr) {
3620         var el = this.getEl();
3621         if (this.patterns.color.test(attr)) {
3622             var val = fly(el).getStyle(attr);
3623
3624             if (this.patterns.transparent.test(val)) {
3625                 var parent = el.parentNode;
3626                 val = fly(parent).getStyle(attr);
3627
3628                 while (parent && this.patterns.transparent.test(val)) {
3629                     parent = parent.parentNode;
3630                     val = fly(parent).getStyle(attr);
3631                     if (parent.tagName.toUpperCase() == 'HTML') {
3632                         val = '#fff';
3633                     }
3634                 }
3635             }
3636         } else {
3637             val = superclass.getAttribute.call(this, attr);
3638         }
3639
3640         return val;
3641     };
3642
3643     proto.doMethod = function(attr, start, end) {
3644         var val;
3645
3646         if (this.patterns.color.test(attr)) {
3647             val = [];
3648             for (var i = 0, len = start.length; i < len; ++i) {
3649                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3650             }
3651
3652             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3653         }
3654         else {
3655             val = superclass.doMethod.call(this, attr, start, end);
3656         }
3657
3658         return val;
3659     };
3660
3661     proto.setRuntimeAttribute = function(attr) {
3662         superclass.setRuntimeAttribute.call(this, attr);
3663
3664         if (this.patterns.color.test(attr)) {
3665             var attributes = this.attributes;
3666             var start = this.parseColor(this.runtimeAttributes[attr].start);
3667             var end = this.parseColor(this.runtimeAttributes[attr].end);
3668
3669             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3670                 end = this.parseColor(attributes[attr].by);
3671
3672                 for (var i = 0, len = start.length; i < len; ++i) {
3673                     end[i] = start[i] + end[i];
3674                 }
3675             }
3676
3677             this.runtimeAttributes[attr].start = start;
3678             this.runtimeAttributes[attr].end = end;
3679         }
3680     };
3681 })();
3682
3683 /*
3684  * Portions of this file are based on pieces of Yahoo User Interface Library
3685  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3686  * YUI licensed under the BSD License:
3687  * http://developer.yahoo.net/yui/license.txt
3688  * <script type="text/javascript">
3689  *
3690  */
3691 Roo.lib.Easing = {
3692
3693
3694     easeNone: function (t, b, c, d) {
3695         return c * t / d + b;
3696     },
3697
3698
3699     easeIn: function (t, b, c, d) {
3700         return c * (t /= d) * t + b;
3701     },
3702
3703
3704     easeOut: function (t, b, c, d) {
3705         return -c * (t /= d) * (t - 2) + b;
3706     },
3707
3708
3709     easeBoth: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t + b;
3712         }
3713
3714         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3715     },
3716
3717
3718     easeInStrong: function (t, b, c, d) {
3719         return c * (t /= d) * t * t * t + b;
3720     },
3721
3722
3723     easeOutStrong: function (t, b, c, d) {
3724         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3725     },
3726
3727
3728     easeBothStrong: function (t, b, c, d) {
3729         if ((t /= d / 2) < 1) {
3730             return c / 2 * t * t * t * t + b;
3731         }
3732
3733         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3734     },
3735
3736
3737
3738     elasticIn: function (t, b, c, d, a, p) {
3739         if (t == 0) {
3740             return b;
3741         }
3742         if ((t /= d) == 1) {
3743             return b + c;
3744         }
3745         if (!p) {
3746             p = d * .3;
3747         }
3748
3749         if (!a || a < Math.abs(c)) {
3750             a = c;
3751             var s = p / 4;
3752         }
3753         else {
3754             var s = p / (2 * Math.PI) * Math.asin(c / a);
3755         }
3756
3757         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3758     },
3759
3760
3761     elasticOut: function (t, b, c, d, a, p) {
3762         if (t == 0) {
3763             return b;
3764         }
3765         if ((t /= d) == 1) {
3766             return b + c;
3767         }
3768         if (!p) {
3769             p = d * .3;
3770         }
3771
3772         if (!a || a < Math.abs(c)) {
3773             a = c;
3774             var s = p / 4;
3775         }
3776         else {
3777             var s = p / (2 * Math.PI) * Math.asin(c / a);
3778         }
3779
3780         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3781     },
3782
3783
3784     elasticBoth: function (t, b, c, d, a, p) {
3785         if (t == 0) {
3786             return b;
3787         }
3788
3789         if ((t /= d / 2) == 2) {
3790             return b + c;
3791         }
3792
3793         if (!p) {
3794             p = d * (.3 * 1.5);
3795         }
3796
3797         if (!a || a < Math.abs(c)) {
3798             a = c;
3799             var s = p / 4;
3800         }
3801         else {
3802             var s = p / (2 * Math.PI) * Math.asin(c / a);
3803         }
3804
3805         if (t < 1) {
3806             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3807                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3808         }
3809         return a * Math.pow(2, -10 * (t -= 1)) *
3810                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3811     },
3812
3813
3814
3815     backIn: function (t, b, c, d, s) {
3816         if (typeof s == 'undefined') {
3817             s = 1.70158;
3818         }
3819         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3820     },
3821
3822
3823     backOut: function (t, b, c, d, s) {
3824         if (typeof s == 'undefined') {
3825             s = 1.70158;
3826         }
3827         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3828     },
3829
3830
3831     backBoth: function (t, b, c, d, s) {
3832         if (typeof s == 'undefined') {
3833             s = 1.70158;
3834         }
3835
3836         if ((t /= d / 2 ) < 1) {
3837             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3838         }
3839         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3840     },
3841
3842
3843     bounceIn: function (t, b, c, d) {
3844         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3845     },
3846
3847
3848     bounceOut: function (t, b, c, d) {
3849         if ((t /= d) < (1 / 2.75)) {
3850             return c * (7.5625 * t * t) + b;
3851         } else if (t < (2 / 2.75)) {
3852             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3853         } else if (t < (2.5 / 2.75)) {
3854             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3855         }
3856         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3857     },
3858
3859
3860     bounceBoth: function (t, b, c, d) {
3861         if (t < d / 2) {
3862             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3863         }
3864         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3865     }
3866 };/*
3867  * Portions of this file are based on pieces of Yahoo User Interface Library
3868  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3869  * YUI licensed under the BSD License:
3870  * http://developer.yahoo.net/yui/license.txt
3871  * <script type="text/javascript">
3872  *
3873  */
3874     (function() {
3875         Roo.lib.Motion = function(el, attributes, duration, method) {
3876             if (el) {
3877                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3878             }
3879         };
3880
3881         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3882
3883
3884         var Y = Roo.lib;
3885         var superclass = Y.Motion.superclass;
3886         var proto = Y.Motion.prototype;
3887
3888         proto.toString = function() {
3889             var el = this.getEl();
3890             var id = el.id || el.tagName;
3891             return ("Motion " + id);
3892         };
3893
3894         proto.patterns.points = /^points$/i;
3895
3896         proto.setAttribute = function(attr, val, unit) {
3897             if (this.patterns.points.test(attr)) {
3898                 unit = unit || 'px';
3899                 superclass.setAttribute.call(this, 'left', val[0], unit);
3900                 superclass.setAttribute.call(this, 'top', val[1], unit);
3901             } else {
3902                 superclass.setAttribute.call(this, attr, val, unit);
3903             }
3904         };
3905
3906         proto.getAttribute = function(attr) {
3907             if (this.patterns.points.test(attr)) {
3908                 var val = [
3909                         superclass.getAttribute.call(this, 'left'),
3910                         superclass.getAttribute.call(this, 'top')
3911                         ];
3912             } else {
3913                 val = superclass.getAttribute.call(this, attr);
3914             }
3915
3916             return val;
3917         };
3918
3919         proto.doMethod = function(attr, start, end) {
3920             var val = null;
3921
3922             if (this.patterns.points.test(attr)) {
3923                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3924                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3925             } else {
3926                 val = superclass.doMethod.call(this, attr, start, end);
3927             }
3928             return val;
3929         };
3930
3931         proto.setRuntimeAttribute = function(attr) {
3932             if (this.patterns.points.test(attr)) {
3933                 var el = this.getEl();
3934                 var attributes = this.attributes;
3935                 var start;
3936                 var control = attributes['points']['control'] || [];
3937                 var end;
3938                 var i, len;
3939
3940                 if (control.length > 0 && !(control[0] instanceof Array)) {
3941                     control = [control];
3942                 } else {
3943                     var tmp = [];
3944                     for (i = 0,len = control.length; i < len; ++i) {
3945                         tmp[i] = control[i];
3946                     }
3947                     control = tmp;
3948                 }
3949
3950                 Roo.fly(el).position();
3951
3952                 if (isset(attributes['points']['from'])) {
3953                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3954                 }
3955                 else {
3956                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3957                 }
3958
3959                 start = this.getAttribute('points');
3960
3961
3962                 if (isset(attributes['points']['to'])) {
3963                     end = translateValues.call(this, attributes['points']['to'], start);
3964
3965                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3966                     for (i = 0,len = control.length; i < len; ++i) {
3967                         control[i] = translateValues.call(this, control[i], start);
3968                     }
3969
3970
3971                 } else if (isset(attributes['points']['by'])) {
3972                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3973
3974                     for (i = 0,len = control.length; i < len; ++i) {
3975                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3976                     }
3977                 }
3978
3979                 this.runtimeAttributes[attr] = [start];
3980
3981                 if (control.length > 0) {
3982                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3983                 }
3984
3985                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3986             }
3987             else {
3988                 superclass.setRuntimeAttribute.call(this, attr);
3989             }
3990         };
3991
3992         var translateValues = function(val, start) {
3993             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3994             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3995
3996             return val;
3997         };
3998
3999         var isset = function(prop) {
4000             return (typeof prop !== 'undefined');
4001         };
4002     })();
4003 /*
4004  * Portions of this file are based on pieces of Yahoo User Interface Library
4005  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4006  * YUI licensed under the BSD License:
4007  * http://developer.yahoo.net/yui/license.txt
4008  * <script type="text/javascript">
4009  *
4010  */
4011     (function() {
4012         Roo.lib.Scroll = function(el, attributes, duration, method) {
4013             if (el) {
4014                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4015             }
4016         };
4017
4018         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4019
4020
4021         var Y = Roo.lib;
4022         var superclass = Y.Scroll.superclass;
4023         var proto = Y.Scroll.prototype;
4024
4025         proto.toString = function() {
4026             var el = this.getEl();
4027             var id = el.id || el.tagName;
4028             return ("Scroll " + id);
4029         };
4030
4031         proto.doMethod = function(attr, start, end) {
4032             var val = null;
4033
4034             if (attr == 'scroll') {
4035                 val = [
4036                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4037                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4038                         ];
4039
4040             } else {
4041                 val = superclass.doMethod.call(this, attr, start, end);
4042             }
4043             return val;
4044         };
4045
4046         proto.getAttribute = function(attr) {
4047             var val = null;
4048             var el = this.getEl();
4049
4050             if (attr == 'scroll') {
4051                 val = [ el.scrollLeft, el.scrollTop ];
4052             } else {
4053                 val = superclass.getAttribute.call(this, attr);
4054             }
4055
4056             return val;
4057         };
4058
4059         proto.setAttribute = function(attr, val, unit) {
4060             var el = this.getEl();
4061
4062             if (attr == 'scroll') {
4063                 el.scrollLeft = val[0];
4064                 el.scrollTop = val[1];
4065             } else {
4066                 superclass.setAttribute.call(this, attr, val, unit);
4067             }
4068         };
4069     })();
4070 /*
4071  * Based on:
4072  * Ext JS Library 1.1.1
4073  * Copyright(c) 2006-2007, Ext JS, LLC.
4074  *
4075  * Originally Released Under LGPL - original licence link has changed is not relivant.
4076  *
4077  * Fork - LGPL
4078  * <script type="text/javascript">
4079  */
4080
4081
4082 // nasty IE9 hack - what a pile of crap that is..
4083
4084  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4085     Range.prototype.createContextualFragment = function (html) {
4086         var doc = window.document;
4087         var container = doc.createElement("div");
4088         container.innerHTML = html;
4089         var frag = doc.createDocumentFragment(), n;
4090         while ((n = container.firstChild)) {
4091             frag.appendChild(n);
4092         }
4093         return frag;
4094     };
4095 }
4096
4097 /**
4098  * @class Roo.DomHelper
4099  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4100  * 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>.
4101  * @singleton
4102  */
4103 Roo.DomHelper = function(){
4104     var tempTableEl = null;
4105     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4106     var tableRe = /^table|tbody|tr|td$/i;
4107     var xmlns = {};
4108     // build as innerHTML where available
4109     /** @ignore */
4110     var createHtml = function(o){
4111         if(typeof o == 'string'){
4112             return o;
4113         }
4114         var b = "";
4115         if(!o.tag){
4116             o.tag = "div";
4117         }
4118         b += "<" + o.tag;
4119         for(var attr in o){
4120             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4121             if(attr == "style"){
4122                 var s = o["style"];
4123                 if(typeof s == "function"){
4124                     s = s.call();
4125                 }
4126                 if(typeof s == "string"){
4127                     b += ' style="' + s + '"';
4128                 }else if(typeof s == "object"){
4129                     b += ' style="';
4130                     for(var key in s){
4131                         if(typeof s[key] != "function"){
4132                             b += key + ":" + s[key] + ";";
4133                         }
4134                     }
4135                     b += '"';
4136                 }
4137             }else{
4138                 if(attr == "cls"){
4139                     b += ' class="' + o["cls"] + '"';
4140                 }else if(attr == "htmlFor"){
4141                     b += ' for="' + o["htmlFor"] + '"';
4142                 }else{
4143                     b += " " + attr + '="' + o[attr] + '"';
4144                 }
4145             }
4146         }
4147         if(emptyTags.test(o.tag)){
4148             b += "/>";
4149         }else{
4150             b += ">";
4151             var cn = o.children || o.cn;
4152             if(cn){
4153                 //http://bugs.kde.org/show_bug.cgi?id=71506
4154                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4155                     for(var i = 0, len = cn.length; i < len; i++) {
4156                         b += createHtml(cn[i], b);
4157                     }
4158                 }else{
4159                     b += createHtml(cn, b);
4160                 }
4161             }
4162             if(o.html){
4163                 b += o.html;
4164             }
4165             b += "</" + o.tag + ">";
4166         }
4167         return b;
4168     };
4169
4170     // build as dom
4171     /** @ignore */
4172     var createDom = function(o, parentNode){
4173          
4174         // defininition craeted..
4175         var ns = false;
4176         if (o.ns && o.ns != 'html') {
4177                
4178             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4179                 xmlns[o.ns] = o.xmlns;
4180                 ns = o.xmlns;
4181             }
4182             if (typeof(xmlns[o.ns]) == 'undefined') {
4183                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4184             }
4185             ns = xmlns[o.ns];
4186         }
4187         
4188         
4189         if (typeof(o) == 'string') {
4190             return parentNode.appendChild(document.createTextNode(o));
4191         }
4192         o.tag = o.tag || div;
4193         if (o.ns && Roo.isIE) {
4194             ns = false;
4195             o.tag = o.ns + ':' + o.tag;
4196             
4197         }
4198         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4199         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4200         for(var attr in o){
4201             
4202             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4203                     attr == "style" || typeof o[attr] == "function") continue;
4204                     
4205             if(attr=="cls" && Roo.isIE){
4206                 el.className = o["cls"];
4207             }else{
4208                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4209                 else el[attr] = o[attr];
4210             }
4211         }
4212         Roo.DomHelper.applyStyles(el, o.style);
4213         var cn = o.children || o.cn;
4214         if(cn){
4215             //http://bugs.kde.org/show_bug.cgi?id=71506
4216              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4217                 for(var i = 0, len = cn.length; i < len; i++) {
4218                     createDom(cn[i], el);
4219                 }
4220             }else{
4221                 createDom(cn, el);
4222             }
4223         }
4224         if(o.html){
4225             el.innerHTML = o.html;
4226         }
4227         if(parentNode){
4228            parentNode.appendChild(el);
4229         }
4230         return el;
4231     };
4232
4233     var ieTable = function(depth, s, h, e){
4234         tempTableEl.innerHTML = [s, h, e].join('');
4235         var i = -1, el = tempTableEl;
4236         while(++i < depth){
4237             el = el.firstChild;
4238         }
4239         return el;
4240     };
4241
4242     // kill repeat to save bytes
4243     var ts = '<table>',
4244         te = '</table>',
4245         tbs = ts+'<tbody>',
4246         tbe = '</tbody>'+te,
4247         trs = tbs + '<tr>',
4248         tre = '</tr>'+tbe;
4249
4250     /**
4251      * @ignore
4252      * Nasty code for IE's broken table implementation
4253      */
4254     var insertIntoTable = function(tag, where, el, html){
4255         if(!tempTableEl){
4256             tempTableEl = document.createElement('div');
4257         }
4258         var node;
4259         var before = null;
4260         if(tag == 'td'){
4261             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4262                 return;
4263             }
4264             if(where == 'beforebegin'){
4265                 before = el;
4266                 el = el.parentNode;
4267             } else{
4268                 before = el.nextSibling;
4269                 el = el.parentNode;
4270             }
4271             node = ieTable(4, trs, html, tre);
4272         }
4273         else if(tag == 'tr'){
4274             if(where == 'beforebegin'){
4275                 before = el;
4276                 el = el.parentNode;
4277                 node = ieTable(3, tbs, html, tbe);
4278             } else if(where == 'afterend'){
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281                 node = ieTable(3, tbs, html, tbe);
4282             } else{ // INTO a TR
4283                 if(where == 'afterbegin'){
4284                     before = el.firstChild;
4285                 }
4286                 node = ieTable(4, trs, html, tre);
4287             }
4288         } else if(tag == 'tbody'){
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292                 node = ieTable(2, ts, html, te);
4293             } else if(where == 'afterend'){
4294                 before = el.nextSibling;
4295                 el = el.parentNode;
4296                 node = ieTable(2, ts, html, te);
4297             } else{
4298                 if(where == 'afterbegin'){
4299                     before = el.firstChild;
4300                 }
4301                 node = ieTable(3, tbs, html, tbe);
4302             }
4303         } else{ // TABLE
4304             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4305                 return;
4306             }
4307             if(where == 'afterbegin'){
4308                 before = el.firstChild;
4309             }
4310             node = ieTable(2, ts, html, te);
4311         }
4312         el.insertBefore(node, before);
4313         return node;
4314     };
4315
4316     return {
4317     /** True to force the use of DOM instead of html fragments @type Boolean */
4318     useDom : false,
4319
4320     /**
4321      * Returns the markup for the passed Element(s) config
4322      * @param {Object} o The Dom object spec (and children)
4323      * @return {String}
4324      */
4325     markup : function(o){
4326         return createHtml(o);
4327     },
4328
4329     /**
4330      * Applies a style specification to an element
4331      * @param {String/HTMLElement} el The element to apply styles to
4332      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4333      * a function which returns such a specification.
4334      */
4335     applyStyles : function(el, styles){
4336         if(styles){
4337            el = Roo.fly(el);
4338            if(typeof styles == "string"){
4339                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4340                var matches;
4341                while ((matches = re.exec(styles)) != null){
4342                    el.setStyle(matches[1], matches[2]);
4343                }
4344            }else if (typeof styles == "object"){
4345                for (var style in styles){
4346                   el.setStyle(style, styles[style]);
4347                }
4348            }else if (typeof styles == "function"){
4349                 Roo.DomHelper.applyStyles(el, styles.call());
4350            }
4351         }
4352     },
4353
4354     /**
4355      * Inserts an HTML fragment into the Dom
4356      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4357      * @param {HTMLElement} el The context element
4358      * @param {String} html The HTML fragmenet
4359      * @return {HTMLElement} The new node
4360      */
4361     insertHtml : function(where, el, html){
4362         where = where.toLowerCase();
4363         if(el.insertAdjacentHTML){
4364             if(tableRe.test(el.tagName)){
4365                 var rs;
4366                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4367                     return rs;
4368                 }
4369             }
4370             switch(where){
4371                 case "beforebegin":
4372                     el.insertAdjacentHTML('BeforeBegin', html);
4373                     return el.previousSibling;
4374                 case "afterbegin":
4375                     el.insertAdjacentHTML('AfterBegin', html);
4376                     return el.firstChild;
4377                 case "beforeend":
4378                     el.insertAdjacentHTML('BeforeEnd', html);
4379                     return el.lastChild;
4380                 case "afterend":
4381                     el.insertAdjacentHTML('AfterEnd', html);
4382                     return el.nextSibling;
4383             }
4384             throw 'Illegal insertion point -> "' + where + '"';
4385         }
4386         var range = el.ownerDocument.createRange();
4387         var frag;
4388         switch(where){
4389              case "beforebegin":
4390                 range.setStartBefore(el);
4391                 frag = range.createContextualFragment(html);
4392                 el.parentNode.insertBefore(frag, el);
4393                 return el.previousSibling;
4394              case "afterbegin":
4395                 if(el.firstChild){
4396                     range.setStartBefore(el.firstChild);
4397                     frag = range.createContextualFragment(html);
4398                     el.insertBefore(frag, el.firstChild);
4399                     return el.firstChild;
4400                 }else{
4401                     el.innerHTML = html;
4402                     return el.firstChild;
4403                 }
4404             case "beforeend":
4405                 if(el.lastChild){
4406                     range.setStartAfter(el.lastChild);
4407                     frag = range.createContextualFragment(html);
4408                     el.appendChild(frag);
4409                     return el.lastChild;
4410                 }else{
4411                     el.innerHTML = html;
4412                     return el.lastChild;
4413                 }
4414             case "afterend":
4415                 range.setStartAfter(el);
4416                 frag = range.createContextualFragment(html);
4417                 el.parentNode.insertBefore(frag, el.nextSibling);
4418                 return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421     },
4422
4423     /**
4424      * Creates new Dom element(s) and inserts them before el
4425      * @param {String/HTMLElement/Element} el The context element
4426      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4427      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4428      * @return {HTMLElement/Roo.Element} The new node
4429      */
4430     insertBefore : function(el, o, returnElement){
4431         return this.doInsert(el, o, returnElement, "beforeBegin");
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them after el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object} o The Dom object spec (and children)
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertAfter : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them as the first child of el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertFirst : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterBegin");
4454     },
4455
4456     // private
4457     doInsert : function(el, o, returnElement, pos, sibling){
4458         el = Roo.getDom(el);
4459         var newNode;
4460         if(this.useDom || o.ns){
4461             newNode = createDom(o, null);
4462             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4463         }else{
4464             var html = createHtml(o);
4465             newNode = this.insertHtml(pos, el, html);
4466         }
4467         return returnElement ? Roo.get(newNode, true) : newNode;
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and appends them to el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     append : function(el, o, returnElement){
4478         el = Roo.getDom(el);
4479         var newNode;
4480         if(this.useDom || o.ns){
4481             newNode = createDom(o, null);
4482             el.appendChild(newNode);
4483         }else{
4484             var html = createHtml(o);
4485             newNode = this.insertHtml("beforeEnd", el, html);
4486         }
4487         return returnElement ? Roo.get(newNode, true) : newNode;
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and overwrites the contents of el with them
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     overwrite : function(el, o, returnElement){
4498         el = Roo.getDom(el);
4499         if (o.ns) {
4500           
4501             while (el.childNodes.length) {
4502                 el.removeChild(el.firstChild);
4503             }
4504             createDom(o, el);
4505         } else {
4506             el.innerHTML = createHtml(o);   
4507         }
4508         
4509         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4510     },
4511
4512     /**
4513      * Creates a new Roo.DomHelper.Template from the Dom object spec
4514      * @param {Object} o The Dom object spec (and children)
4515      * @return {Roo.DomHelper.Template} The new template
4516      */
4517     createTemplate : function(o){
4518         var html = createHtml(o);
4519         return new Roo.Template(html);
4520     }
4521     };
4522 }();
4523 /*
4524  * Based on:
4525  * Ext JS Library 1.1.1
4526  * Copyright(c) 2006-2007, Ext JS, LLC.
4527  *
4528  * Originally Released Under LGPL - original licence link has changed is not relivant.
4529  *
4530  * Fork - LGPL
4531  * <script type="text/javascript">
4532  */
4533  
4534 /**
4535 * @class Roo.Template
4536 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4537 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4538 * Usage:
4539 <pre><code>
4540 var t = new Roo.Template({
4541     html :  '&lt;div name="{id}"&gt;' + 
4542         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4543         '&lt;/div&gt;',
4544     myformat: function (value, allValues) {
4545         return 'XX' + value;
4546     }
4547 });
4548 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4549 </code></pre>
4550 * For more information see this blog post with examples:
4551 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4552      - Create Elements using DOM, HTML fragments and Templates</a>. 
4553 * @constructor
4554 * @param {Object} cfg - Configuration object.
4555 */
4556 Roo.Template = function(cfg){
4557     // BC!
4558     if(cfg instanceof Array){
4559         cfg = cfg.join("");
4560     }else if(arguments.length > 1){
4561         cfg = Array.prototype.join.call(arguments, "");
4562     }
4563     
4564     
4565     if (typeof(cfg) == 'object') {
4566         Roo.apply(this,cfg)
4567     } else {
4568         // bc
4569         this.html = cfg;
4570     }
4571     if (this.url) {
4572         this.load();
4573     }
4574     
4575 };
4576 Roo.Template.prototype = {
4577     
4578     /**
4579      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4580      *                    it should be fixed so that template is observable...
4581      */
4582     url : false,
4583     /**
4584      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4585      */
4586     html : '',
4587     /**
4588      * Returns an HTML fragment of this template with the specified values applied.
4589      * @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'})
4590      * @return {String} The HTML fragment
4591      */
4592     applyTemplate : function(values){
4593         try {
4594            
4595             if(this.compiled){
4596                 return this.compiled(values);
4597             }
4598             var useF = this.disableFormats !== true;
4599             var fm = Roo.util.Format, tpl = this;
4600             var fn = function(m, name, format, args){
4601                 if(format && useF){
4602                     if(format.substr(0, 5) == "this."){
4603                         return tpl.call(format.substr(5), values[name], values);
4604                     }else{
4605                         if(args){
4606                             // quoted values are required for strings in compiled templates, 
4607                             // but for non compiled we need to strip them
4608                             // quoted reversed for jsmin
4609                             var re = /^\s*['"](.*)["']\s*$/;
4610                             args = args.split(',');
4611                             for(var i = 0, len = args.length; i < len; i++){
4612                                 args[i] = args[i].replace(re, "$1");
4613                             }
4614                             args = [values[name]].concat(args);
4615                         }else{
4616                             args = [values[name]];
4617                         }
4618                         return fm[format].apply(fm, args);
4619                     }
4620                 }else{
4621                     return values[name] !== undefined ? values[name] : "";
4622                 }
4623             };
4624             return this.html.replace(this.re, fn);
4625         } catch (e) {
4626             Roo.log(e);
4627             throw e;
4628         }
4629          
4630     },
4631     
4632     loading : false,
4633       
4634     load : function ()
4635     {
4636          
4637         if (this.loading) {
4638             return;
4639         }
4640         var _t = this;
4641         
4642         this.loading = true;
4643         this.compiled = false;
4644         
4645         var cx = new Roo.data.Connection();
4646         cx.request({
4647             url : this.url,
4648             method : 'GET',
4649             success : function (response) {
4650                 _t.loading = false;
4651                 _t.html = response.responseText;
4652                 _t.url = false;
4653                 _t.compile();
4654              },
4655             failure : function(response) {
4656                 Roo.log("Template failed to load from " + url);
4657                 _t.loading = false;
4658             }
4659         });
4660     },
4661
4662     /**
4663      * Sets the HTML used as the template and optionally compiles it.
4664      * @param {String} html
4665      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4666      * @return {Roo.Template} this
4667      */
4668     set : function(html, compile){
4669         this.html = html;
4670         this.compiled = null;
4671         if(compile){
4672             this.compile();
4673         }
4674         return this;
4675     },
4676     
4677     /**
4678      * True to disable format functions (defaults to false)
4679      * @type Boolean
4680      */
4681     disableFormats : false,
4682     
4683     /**
4684     * The regular expression used to match template variables 
4685     * @type RegExp
4686     * @property 
4687     */
4688     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4689     
4690     /**
4691      * Compiles the template into an internal function, eliminating the RegEx overhead.
4692      * @return {Roo.Template} this
4693      */
4694     compile : function(){
4695         var fm = Roo.util.Format;
4696         var useF = this.disableFormats !== true;
4697         var sep = Roo.isGecko ? "+" : ",";
4698         var fn = function(m, name, format, args){
4699             if(format && useF){
4700                 args = args ? ',' + args : "";
4701                 if(format.substr(0, 5) != "this."){
4702                     format = "fm." + format + '(';
4703                 }else{
4704                     format = 'this.call("'+ format.substr(5) + '", ';
4705                     args = ", values";
4706                 }
4707             }else{
4708                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4709             }
4710             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4711         };
4712         var body;
4713         // branched to use + in gecko and [].join() in others
4714         if(Roo.isGecko){
4715             body = "this.compiled = function(values){ return '" +
4716                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4717                     "';};";
4718         }else{
4719             body = ["this.compiled = function(values){ return ['"];
4720             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4721             body.push("'].join('');};");
4722             body = body.join('');
4723         }
4724         /**
4725          * eval:var:values
4726          * eval:var:fm
4727          */
4728         eval(body);
4729         return this;
4730     },
4731     
4732     // private function used to call members
4733     call : function(fnName, value, allValues){
4734         return this[fnName](value, allValues);
4735     },
4736     
4737     /**
4738      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4739      * @param {String/HTMLElement/Roo.Element} el The context element
4740      * @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'})
4741      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4742      * @return {HTMLElement/Roo.Element} The new node or Element
4743      */
4744     insertFirst: function(el, values, returnElement){
4745         return this.doInsert('afterBegin', el, values, returnElement);
4746     },
4747
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) before el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @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'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertBefore: function(el, values, returnElement){
4756         return this.doInsert('beforeBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) after el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @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'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertAfter : function(el, values, returnElement){
4767         return this.doInsert('afterEnd', el, values, returnElement);
4768     },
4769     
4770     /**
4771      * Applies the supplied values to the template and appends the new node(s) to el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     append : function(el, values, returnElement){
4778         return this.doInsert('beforeEnd', el, values, returnElement);
4779     },
4780
4781     doInsert : function(where, el, values, returnEl){
4782         el = Roo.getDom(el);
4783         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4784         return returnEl ? Roo.get(newNode, true) : newNode;
4785     },
4786
4787     /**
4788      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4789      * @param {String/HTMLElement/Roo.Element} el The context element
4790      * @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'})
4791      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4792      * @return {HTMLElement/Roo.Element} The new node or Element
4793      */
4794     overwrite : function(el, values, returnElement){
4795         el = Roo.getDom(el);
4796         el.innerHTML = this.applyTemplate(values);
4797         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4798     }
4799 };
4800 /**
4801  * Alias for {@link #applyTemplate}
4802  * @method
4803  */
4804 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4805
4806 // backwards compat
4807 Roo.DomHelper.Template = Roo.Template;
4808
4809 /**
4810  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4811  * @param {String/HTMLElement} el A DOM element or its id
4812  * @returns {Roo.Template} The created template
4813  * @static
4814  */
4815 Roo.Template.from = function(el){
4816     el = Roo.getDom(el);
4817     return new Roo.Template(el.value || el.innerHTML);
4818 };/*
4819  * Based on:
4820  * Ext JS Library 1.1.1
4821  * Copyright(c) 2006-2007, Ext JS, LLC.
4822  *
4823  * Originally Released Under LGPL - original licence link has changed is not relivant.
4824  *
4825  * Fork - LGPL
4826  * <script type="text/javascript">
4827  */
4828  
4829
4830 /*
4831  * This is code is also distributed under MIT license for use
4832  * with jQuery and prototype JavaScript libraries.
4833  */
4834 /**
4835  * @class Roo.DomQuery
4836 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).
4837 <p>
4838 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>
4839
4840 <p>
4841 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.
4842 </p>
4843 <h4>Element Selectors:</h4>
4844 <ul class="list">
4845     <li> <b>*</b> any element</li>
4846     <li> <b>E</b> an element with the tag E</li>
4847     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4848     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4849     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4850     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4851 </ul>
4852 <h4>Attribute Selectors:</h4>
4853 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4854 <ul class="list">
4855     <li> <b>E[foo]</b> has an attribute "foo"</li>
4856     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4857     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4858     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4859     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4860     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4861     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4862 </ul>
4863 <h4>Pseudo Classes:</h4>
4864 <ul class="list">
4865     <li> <b>E:first-child</b> E is the first child of its parent</li>
4866     <li> <b>E:last-child</b> E is the last child of its parent</li>
4867     <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>
4868     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4869     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4870     <li> <b>E:only-child</b> E is the only child of its parent</li>
4871     <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>
4872     <li> <b>E:first</b> the first E in the resultset</li>
4873     <li> <b>E:last</b> the last E in the resultset</li>
4874     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4875     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4876     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4877     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4878     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4879     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4880     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4881     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4882     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4883 </ul>
4884 <h4>CSS Value Selectors:</h4>
4885 <ul class="list">
4886     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4887     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4888     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4889     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4890     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4891     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4892 </ul>
4893  * @singleton
4894  */
4895 Roo.DomQuery = function(){
4896     var cache = {}, simpleCache = {}, valueCache = {};
4897     var nonSpace = /\S/;
4898     var trimRe = /^\s+|\s+$/g;
4899     var tplRe = /\{(\d+)\}/g;
4900     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4901     var tagTokenRe = /^(#)?([\w-\*]+)/;
4902     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4903
4904     function child(p, index){
4905         var i = 0;
4906         var n = p.firstChild;
4907         while(n){
4908             if(n.nodeType == 1){
4909                if(++i == index){
4910                    return n;
4911                }
4912             }
4913             n = n.nextSibling;
4914         }
4915         return null;
4916     };
4917
4918     function next(n){
4919         while((n = n.nextSibling) && n.nodeType != 1);
4920         return n;
4921     };
4922
4923     function prev(n){
4924         while((n = n.previousSibling) && n.nodeType != 1);
4925         return n;
4926     };
4927
4928     function children(d){
4929         var n = d.firstChild, ni = -1;
4930             while(n){
4931                 var nx = n.nextSibling;
4932                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4933                     d.removeChild(n);
4934                 }else{
4935                     n.nodeIndex = ++ni;
4936                 }
4937                 n = nx;
4938             }
4939             return this;
4940         };
4941
4942     function byClassName(c, a, v){
4943         if(!v){
4944             return c;
4945         }
4946         var r = [], ri = -1, cn;
4947         for(var i = 0, ci; ci = c[i]; i++){
4948             if((' '+ci.className+' ').indexOf(v) != -1){
4949                 r[++ri] = ci;
4950             }
4951         }
4952         return r;
4953     };
4954
4955     function attrValue(n, attr){
4956         if(!n.tagName && typeof n.length != "undefined"){
4957             n = n[0];
4958         }
4959         if(!n){
4960             return null;
4961         }
4962         if(attr == "for"){
4963             return n.htmlFor;
4964         }
4965         if(attr == "class" || attr == "className"){
4966             return n.className;
4967         }
4968         return n.getAttribute(attr) || n[attr];
4969
4970     };
4971
4972     function getNodes(ns, mode, tagName){
4973         var result = [], ri = -1, cs;
4974         if(!ns){
4975             return result;
4976         }
4977         tagName = tagName || "*";
4978         if(typeof ns.getElementsByTagName != "undefined"){
4979             ns = [ns];
4980         }
4981         if(!mode){
4982             for(var i = 0, ni; ni = ns[i]; i++){
4983                 cs = ni.getElementsByTagName(tagName);
4984                 for(var j = 0, ci; ci = cs[j]; j++){
4985                     result[++ri] = ci;
4986                 }
4987             }
4988         }else if(mode == "/" || mode == ">"){
4989             var utag = tagName.toUpperCase();
4990             for(var i = 0, ni, cn; ni = ns[i]; i++){
4991                 cn = ni.children || ni.childNodes;
4992                 for(var j = 0, cj; cj = cn[j]; j++){
4993                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4994                         result[++ri] = cj;
4995                     }
4996                 }
4997             }
4998         }else if(mode == "+"){
4999             var utag = tagName.toUpperCase();
5000             for(var i = 0, n; n = ns[i]; i++){
5001                 while((n = n.nextSibling) && n.nodeType != 1);
5002                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5003                     result[++ri] = n;
5004                 }
5005             }
5006         }else if(mode == "~"){
5007             for(var i = 0, n; n = ns[i]; i++){
5008                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5009                 if(n){
5010                     result[++ri] = n;
5011                 }
5012             }
5013         }
5014         return result;
5015     };
5016
5017     function concat(a, b){
5018         if(b.slice){
5019             return a.concat(b);
5020         }
5021         for(var i = 0, l = b.length; i < l; i++){
5022             a[a.length] = b[i];
5023         }
5024         return a;
5025     }
5026
5027     function byTag(cs, tagName){
5028         if(cs.tagName || cs == document){
5029             cs = [cs];
5030         }
5031         if(!tagName){
5032             return cs;
5033         }
5034         var r = [], ri = -1;
5035         tagName = tagName.toLowerCase();
5036         for(var i = 0, ci; ci = cs[i]; i++){
5037             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5038                 r[++ri] = ci;
5039             }
5040         }
5041         return r;
5042     };
5043
5044     function byId(cs, attr, id){
5045         if(cs.tagName || cs == document){
5046             cs = [cs];
5047         }
5048         if(!id){
5049             return cs;
5050         }
5051         var r = [], ri = -1;
5052         for(var i = 0,ci; ci = cs[i]; i++){
5053             if(ci && ci.id == id){
5054                 r[++ri] = ci;
5055                 return r;
5056             }
5057         }
5058         return r;
5059     };
5060
5061     function byAttribute(cs, attr, value, op, custom){
5062         var r = [], ri = -1, st = custom=="{";
5063         var f = Roo.DomQuery.operators[op];
5064         for(var i = 0, ci; ci = cs[i]; i++){
5065             var a;
5066             if(st){
5067                 a = Roo.DomQuery.getStyle(ci, attr);
5068             }
5069             else if(attr == "class" || attr == "className"){
5070                 a = ci.className;
5071             }else if(attr == "for"){
5072                 a = ci.htmlFor;
5073             }else if(attr == "href"){
5074                 a = ci.getAttribute("href", 2);
5075             }else{
5076                 a = ci.getAttribute(attr);
5077             }
5078             if((f && f(a, value)) || (!f && a)){
5079                 r[++ri] = ci;
5080             }
5081         }
5082         return r;
5083     };
5084
5085     function byPseudo(cs, name, value){
5086         return Roo.DomQuery.pseudos[name](cs, value);
5087     };
5088
5089     // This is for IE MSXML which does not support expandos.
5090     // IE runs the same speed using setAttribute, however FF slows way down
5091     // and Safari completely fails so they need to continue to use expandos.
5092     var isIE = window.ActiveXObject ? true : false;
5093
5094     // this eval is stop the compressor from
5095     // renaming the variable to something shorter
5096     
5097     /** eval:var:batch */
5098     var batch = 30803; 
5099
5100     var key = 30803;
5101
5102     function nodupIEXml(cs){
5103         var d = ++key;
5104         cs[0].setAttribute("_nodup", d);
5105         var r = [cs[0]];
5106         for(var i = 1, len = cs.length; i < len; i++){
5107             var c = cs[i];
5108             if(!c.getAttribute("_nodup") != d){
5109                 c.setAttribute("_nodup", d);
5110                 r[r.length] = c;
5111             }
5112         }
5113         for(var i = 0, len = cs.length; i < len; i++){
5114             cs[i].removeAttribute("_nodup");
5115         }
5116         return r;
5117     }
5118
5119     function nodup(cs){
5120         if(!cs){
5121             return [];
5122         }
5123         var len = cs.length, c, i, r = cs, cj, ri = -1;
5124         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5125             return cs;
5126         }
5127         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5128             return nodupIEXml(cs);
5129         }
5130         var d = ++key;
5131         cs[0]._nodup = d;
5132         for(i = 1; c = cs[i]; i++){
5133             if(c._nodup != d){
5134                 c._nodup = d;
5135             }else{
5136                 r = [];
5137                 for(var j = 0; j < i; j++){
5138                     r[++ri] = cs[j];
5139                 }
5140                 for(j = i+1; cj = cs[j]; j++){
5141                     if(cj._nodup != d){
5142                         cj._nodup = d;
5143                         r[++ri] = cj;
5144                     }
5145                 }
5146                 return r;
5147             }
5148         }
5149         return r;
5150     }
5151
5152     function quickDiffIEXml(c1, c2){
5153         var d = ++key;
5154         for(var i = 0, len = c1.length; i < len; i++){
5155             c1[i].setAttribute("_qdiff", d);
5156         }
5157         var r = [];
5158         for(var i = 0, len = c2.length; i < len; i++){
5159             if(c2[i].getAttribute("_qdiff") != d){
5160                 r[r.length] = c2[i];
5161             }
5162         }
5163         for(var i = 0, len = c1.length; i < len; i++){
5164            c1[i].removeAttribute("_qdiff");
5165         }
5166         return r;
5167     }
5168
5169     function quickDiff(c1, c2){
5170         var len1 = c1.length;
5171         if(!len1){
5172             return c2;
5173         }
5174         if(isIE && c1[0].selectSingleNode){
5175             return quickDiffIEXml(c1, c2);
5176         }
5177         var d = ++key;
5178         for(var i = 0; i < len1; i++){
5179             c1[i]._qdiff = d;
5180         }
5181         var r = [];
5182         for(var i = 0, len = c2.length; i < len; i++){
5183             if(c2[i]._qdiff != d){
5184                 r[r.length] = c2[i];
5185             }
5186         }
5187         return r;
5188     }
5189
5190     function quickId(ns, mode, root, id){
5191         if(ns == root){
5192            var d = root.ownerDocument || root;
5193            return d.getElementById(id);
5194         }
5195         ns = getNodes(ns, mode, "*");
5196         return byId(ns, null, id);
5197     }
5198
5199     return {
5200         getStyle : function(el, name){
5201             return Roo.fly(el).getStyle(name);
5202         },
5203         /**
5204          * Compiles a selector/xpath query into a reusable function. The returned function
5205          * takes one parameter "root" (optional), which is the context node from where the query should start.
5206          * @param {String} selector The selector/xpath query
5207          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5208          * @return {Function}
5209          */
5210         compile : function(path, type){
5211             type = type || "select";
5212             
5213             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5214             var q = path, mode, lq;
5215             var tk = Roo.DomQuery.matchers;
5216             var tklen = tk.length;
5217             var mm;
5218
5219             // accept leading mode switch
5220             var lmode = q.match(modeRe);
5221             if(lmode && lmode[1]){
5222                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5223                 q = q.replace(lmode[1], "");
5224             }
5225             // strip leading slashes
5226             while(path.substr(0, 1)=="/"){
5227                 path = path.substr(1);
5228             }
5229
5230             while(q && lq != q){
5231                 lq = q;
5232                 var tm = q.match(tagTokenRe);
5233                 if(type == "select"){
5234                     if(tm){
5235                         if(tm[1] == "#"){
5236                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5237                         }else{
5238                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5239                         }
5240                         q = q.replace(tm[0], "");
5241                     }else if(q.substr(0, 1) != '@'){
5242                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5243                     }
5244                 }else{
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }
5253                 }
5254                 while(!(mm = q.match(modeRe))){
5255                     var matched = false;
5256                     for(var j = 0; j < tklen; j++){
5257                         var t = tk[j];
5258                         var m = q.match(t.re);
5259                         if(m){
5260                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5261                                                     return m[i];
5262                                                 });
5263                             q = q.replace(m[0], "");
5264                             matched = true;
5265                             break;
5266                         }
5267                     }
5268                     // prevent infinite loop on bad selector
5269                     if(!matched){
5270                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5271                     }
5272                 }
5273                 if(mm[1]){
5274                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5275                     q = q.replace(mm[1], "");
5276                 }
5277             }
5278             fn[fn.length] = "return nodup(n);\n}";
5279             
5280              /** 
5281               * list of variables that need from compression as they are used by eval.
5282              *  eval:var:batch 
5283              *  eval:var:nodup
5284              *  eval:var:byTag
5285              *  eval:var:ById
5286              *  eval:var:getNodes
5287              *  eval:var:quickId
5288              *  eval:var:mode
5289              *  eval:var:root
5290              *  eval:var:n
5291              *  eval:var:byClassName
5292              *  eval:var:byPseudo
5293              *  eval:var:byAttribute
5294              *  eval:var:attrValue
5295              * 
5296              **/ 
5297             eval(fn.join(""));
5298             return f;
5299         },
5300
5301         /**
5302          * Selects a group of elements.
5303          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5304          * @param {Node} root (optional) The start of the query (defaults to document).
5305          * @return {Array}
5306          */
5307         select : function(path, root, type){
5308             if(!root || root == document){
5309                 root = document;
5310             }
5311             if(typeof root == "string"){
5312                 root = document.getElementById(root);
5313             }
5314             var paths = path.split(",");
5315             var results = [];
5316             for(var i = 0, len = paths.length; i < len; i++){
5317                 var p = paths[i].replace(trimRe, "");
5318                 if(!cache[p]){
5319                     cache[p] = Roo.DomQuery.compile(p);
5320                     if(!cache[p]){
5321                         throw p + " is not a valid selector";
5322                     }
5323                 }
5324                 var result = cache[p](root);
5325                 if(result && result != document){
5326                     results = results.concat(result);
5327                 }
5328             }
5329             if(paths.length > 1){
5330                 return nodup(results);
5331             }
5332             return results;
5333         },
5334
5335         /**
5336          * Selects a single element.
5337          * @param {String} selector The selector/xpath query
5338          * @param {Node} root (optional) The start of the query (defaults to document).
5339          * @return {Element}
5340          */
5341         selectNode : function(path, root){
5342             return Roo.DomQuery.select(path, root)[0];
5343         },
5344
5345         /**
5346          * Selects the value of a node, optionally replacing null with the defaultValue.
5347          * @param {String} selector The selector/xpath query
5348          * @param {Node} root (optional) The start of the query (defaults to document).
5349          * @param {String} defaultValue
5350          */
5351         selectValue : function(path, root, defaultValue){
5352             path = path.replace(trimRe, "");
5353             if(!valueCache[path]){
5354                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5355             }
5356             var n = valueCache[path](root);
5357             n = n[0] ? n[0] : n;
5358             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5359             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5360         },
5361
5362         /**
5363          * Selects the value of a node, parsing integers and floats.
5364          * @param {String} selector The selector/xpath query
5365          * @param {Node} root (optional) The start of the query (defaults to document).
5366          * @param {Number} defaultValue
5367          * @return {Number}
5368          */
5369         selectNumber : function(path, root, defaultValue){
5370             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5371             return parseFloat(v);
5372         },
5373
5374         /**
5375          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5376          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5377          * @param {String} selector The simple selector to test
5378          * @return {Boolean}
5379          */
5380         is : function(el, ss){
5381             if(typeof el == "string"){
5382                 el = document.getElementById(el);
5383             }
5384             var isArray = (el instanceof Array);
5385             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5386             return isArray ? (result.length == el.length) : (result.length > 0);
5387         },
5388
5389         /**
5390          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5391          * @param {Array} el An array of elements to filter
5392          * @param {String} selector The simple selector to test
5393          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5394          * the selector instead of the ones that match
5395          * @return {Array}
5396          */
5397         filter : function(els, ss, nonMatches){
5398             ss = ss.replace(trimRe, "");
5399             if(!simpleCache[ss]){
5400                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5401             }
5402             var result = simpleCache[ss](els);
5403             return nonMatches ? quickDiff(result, els) : result;
5404         },
5405
5406         /**
5407          * Collection of matching regular expressions and code snippets.
5408          */
5409         matchers : [{
5410                 re: /^\.([\w-]+)/,
5411                 select: 'n = byClassName(n, null, " {1} ");'
5412             }, {
5413                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5414                 select: 'n = byPseudo(n, "{1}", "{2}");'
5415             },{
5416                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5417                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5418             }, {
5419                 re: /^#([\w-]+)/,
5420                 select: 'n = byId(n, null, "{1}");'
5421             },{
5422                 re: /^@([\w-]+)/,
5423                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5424             }
5425         ],
5426
5427         /**
5428          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5429          * 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;.
5430          */
5431         operators : {
5432             "=" : function(a, v){
5433                 return a == v;
5434             },
5435             "!=" : function(a, v){
5436                 return a != v;
5437             },
5438             "^=" : function(a, v){
5439                 return a && a.substr(0, v.length) == v;
5440             },
5441             "$=" : function(a, v){
5442                 return a && a.substr(a.length-v.length) == v;
5443             },
5444             "*=" : function(a, v){
5445                 return a && a.indexOf(v) !== -1;
5446             },
5447             "%=" : function(a, v){
5448                 return (a % v) == 0;
5449             },
5450             "|=" : function(a, v){
5451                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5452             },
5453             "~=" : function(a, v){
5454                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5455             }
5456         },
5457
5458         /**
5459          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5460          * and the argument (if any) supplied in the selector.
5461          */
5462         pseudos : {
5463             "first-child" : function(c){
5464                 var r = [], ri = -1, n;
5465                 for(var i = 0, ci; ci = n = c[i]; i++){
5466                     while((n = n.previousSibling) && n.nodeType != 1);
5467                     if(!n){
5468                         r[++ri] = ci;
5469                     }
5470                 }
5471                 return r;
5472             },
5473
5474             "last-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.nextSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "nth-child" : function(c, a) {
5486                 var r = [], ri = -1;
5487                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5488                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5489                 for(var i = 0, n; n = c[i]; i++){
5490                     var pn = n.parentNode;
5491                     if (batch != pn._batch) {
5492                         var j = 0;
5493                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5494                             if(cn.nodeType == 1){
5495                                cn.nodeIndex = ++j;
5496                             }
5497                         }
5498                         pn._batch = batch;
5499                     }
5500                     if (f == 1) {
5501                         if (l == 0 || n.nodeIndex == l){
5502                             r[++ri] = n;
5503                         }
5504                     } else if ((n.nodeIndex + l) % f == 0){
5505                         r[++ri] = n;
5506                     }
5507                 }
5508
5509                 return r;
5510             },
5511
5512             "only-child" : function(c){
5513                 var r = [], ri = -1;;
5514                 for(var i = 0, ci; ci = c[i]; i++){
5515                     if(!prev(ci) && !next(ci)){
5516                         r[++ri] = ci;
5517                     }
5518                 }
5519                 return r;
5520             },
5521
5522             "empty" : function(c){
5523                 var r = [], ri = -1;
5524                 for(var i = 0, ci; ci = c[i]; i++){
5525                     var cns = ci.childNodes, j = 0, cn, empty = true;
5526                     while(cn = cns[j]){
5527                         ++j;
5528                         if(cn.nodeType == 1 || cn.nodeType == 3){
5529                             empty = false;
5530                             break;
5531                         }
5532                     }
5533                     if(empty){
5534                         r[++ri] = ci;
5535                     }
5536                 }
5537                 return r;
5538             },
5539
5540             "contains" : function(c, v){
5541                 var r = [], ri = -1;
5542                 for(var i = 0, ci; ci = c[i]; i++){
5543                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5544                         r[++ri] = ci;
5545                     }
5546                 }
5547                 return r;
5548             },
5549
5550             "nodeValue" : function(c, v){
5551                 var r = [], ri = -1;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5554                         r[++ri] = ci;
5555                     }
5556                 }
5557                 return r;
5558             },
5559
5560             "checked" : function(c){
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     if(ci.checked == true){
5564                         r[++ri] = ci;
5565                     }
5566                 }
5567                 return r;
5568             },
5569
5570             "not" : function(c, ss){
5571                 return Roo.DomQuery.filter(c, ss, true);
5572             },
5573
5574             "odd" : function(c){
5575                 return this["nth-child"](c, "odd");
5576             },
5577
5578             "even" : function(c){
5579                 return this["nth-child"](c, "even");
5580             },
5581
5582             "nth" : function(c, a){
5583                 return c[a-1] || [];
5584             },
5585
5586             "first" : function(c){
5587                 return c[0] || [];
5588             },
5589
5590             "last" : function(c){
5591                 return c[c.length-1] || [];
5592             },
5593
5594             "has" : function(c, ss){
5595                 var s = Roo.DomQuery.select;
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(s(ss, ci).length > 0){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "next" : function(c, ss){
5606                 var is = Roo.DomQuery.is;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     var n = next(ci);
5610                     if(n && is(n, ss)){
5611                         r[++ri] = ci;
5612                     }
5613                 }
5614                 return r;
5615             },
5616
5617             "prev" : function(c, ss){
5618                 var is = Roo.DomQuery.is;
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     var n = prev(ci);
5622                     if(n && is(n, ss)){
5623                         r[++ri] = ci;
5624                     }
5625                 }
5626                 return r;
5627             }
5628         }
5629     };
5630 }();
5631
5632 /**
5633  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5634  * @param {String} path The selector/xpath query
5635  * @param {Node} root (optional) The start of the query (defaults to document).
5636  * @return {Array}
5637  * @member Roo
5638  * @method query
5639  */
5640 Roo.query = Roo.DomQuery.select;
5641 /*
5642  * Based on:
5643  * Ext JS Library 1.1.1
5644  * Copyright(c) 2006-2007, Ext JS, LLC.
5645  *
5646  * Originally Released Under LGPL - original licence link has changed is not relivant.
5647  *
5648  * Fork - LGPL
5649  * <script type="text/javascript">
5650  */
5651
5652 /**
5653  * @class Roo.util.Observable
5654  * Base class that provides a common interface for publishing events. Subclasses are expected to
5655  * to have a property "events" with all the events defined.<br>
5656  * For example:
5657  * <pre><code>
5658  Employee = function(name){
5659     this.name = name;
5660     this.addEvents({
5661         "fired" : true,
5662         "quit" : true
5663     });
5664  }
5665  Roo.extend(Employee, Roo.util.Observable);
5666 </code></pre>
5667  * @param {Object} config properties to use (incuding events / listeners)
5668  */
5669
5670 Roo.util.Observable = function(cfg){
5671     
5672     cfg = cfg|| {};
5673     this.addEvents(cfg.events || {});
5674     if (cfg.events) {
5675         delete cfg.events; // make sure
5676     }
5677      
5678     Roo.apply(this, cfg);
5679     
5680     if(this.listeners){
5681         this.on(this.listeners);
5682         delete this.listeners;
5683     }
5684 };
5685 Roo.util.Observable.prototype = {
5686     /** 
5687  * @cfg {Object} listeners  list of events and functions to call for this object, 
5688  * For example :
5689  * <pre><code>
5690     listeners :  { 
5691        'click' : function(e) {
5692            ..... 
5693         } ,
5694         .... 
5695     } 
5696   </code></pre>
5697  */
5698     
5699     
5700     /**
5701      * Fires the specified event with the passed parameters (minus the event name).
5702      * @param {String} eventName
5703      * @param {Object...} args Variable number of parameters are passed to handlers
5704      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5705      */
5706     fireEvent : function(){
5707         var ce = this.events[arguments[0].toLowerCase()];
5708         if(typeof ce == "object"){
5709             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5710         }else{
5711             return true;
5712         }
5713     },
5714
5715     // private
5716     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5717
5718     /**
5719      * Appends an event handler to this component
5720      * @param {String}   eventName The type of event to listen for
5721      * @param {Function} handler The method the event invokes
5722      * @param {Object}   scope (optional) The scope in which to execute the handler
5723      * function. The handler function's "this" context.
5724      * @param {Object}   options (optional) An object containing handler configuration
5725      * properties. This may contain any of the following properties:<ul>
5726      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5727      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5728      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5729      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5730      * by the specified number of milliseconds. If the event fires again within that time, the original
5731      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5732      * </ul><br>
5733      * <p>
5734      * <b>Combining Options</b><br>
5735      * Using the options argument, it is possible to combine different types of listeners:<br>
5736      * <br>
5737      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5738                 <pre><code>
5739                 el.on('click', this.onClick, this, {
5740                         single: true,
5741                 delay: 100,
5742                 forumId: 4
5743                 });
5744                 </code></pre>
5745      * <p>
5746      * <b>Attaching multiple handlers in 1 call</b><br>
5747      * The method also allows for a single argument to be passed which is a config object containing properties
5748      * which specify multiple handlers.
5749      * <pre><code>
5750                 el.on({
5751                         'click': {
5752                         fn: this.onClick,
5753                         scope: this,
5754                         delay: 100
5755                 }, 
5756                 'mouseover': {
5757                         fn: this.onMouseOver,
5758                         scope: this
5759                 },
5760                 'mouseout': {
5761                         fn: this.onMouseOut,
5762                         scope: this
5763                 }
5764                 });
5765                 </code></pre>
5766      * <p>
5767      * Or a shorthand syntax which passes the same scope object to all handlers:
5768         <pre><code>
5769                 el.on({
5770                         'click': this.onClick,
5771                 'mouseover': this.onMouseOver,
5772                 'mouseout': this.onMouseOut,
5773                 scope: this
5774                 });
5775                 </code></pre>
5776      */
5777     addListener : function(eventName, fn, scope, o){
5778         if(typeof eventName == "object"){
5779             o = eventName;
5780             for(var e in o){
5781                 if(this.filterOptRe.test(e)){
5782                     continue;
5783                 }
5784                 if(typeof o[e] == "function"){
5785                     // shared options
5786                     this.addListener(e, o[e], o.scope,  o);
5787                 }else{
5788                     // individual options
5789                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5790                 }
5791             }
5792             return;
5793         }
5794         o = (!o || typeof o == "boolean") ? {} : o;
5795         eventName = eventName.toLowerCase();
5796         var ce = this.events[eventName] || true;
5797         if(typeof ce == "boolean"){
5798             ce = new Roo.util.Event(this, eventName);
5799             this.events[eventName] = ce;
5800         }
5801         ce.addListener(fn, scope, o);
5802     },
5803
5804     /**
5805      * Removes a listener
5806      * @param {String}   eventName     The type of event to listen for
5807      * @param {Function} handler        The handler to remove
5808      * @param {Object}   scope  (optional) The scope (this object) for the handler
5809      */
5810     removeListener : function(eventName, fn, scope){
5811         var ce = this.events[eventName.toLowerCase()];
5812         if(typeof ce == "object"){
5813             ce.removeListener(fn, scope);
5814         }
5815     },
5816
5817     /**
5818      * Removes all listeners for this object
5819      */
5820     purgeListeners : function(){
5821         for(var evt in this.events){
5822             if(typeof this.events[evt] == "object"){
5823                  this.events[evt].clearListeners();
5824             }
5825         }
5826     },
5827
5828     relayEvents : function(o, events){
5829         var createHandler = function(ename){
5830             return function(){
5831                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5832             };
5833         };
5834         for(var i = 0, len = events.length; i < len; i++){
5835             var ename = events[i];
5836             if(!this.events[ename]){ this.events[ename] = true; };
5837             o.on(ename, createHandler(ename), this);
5838         }
5839     },
5840
5841     /**
5842      * Used to define events on this Observable
5843      * @param {Object} object The object with the events defined
5844      */
5845     addEvents : function(o){
5846         if(!this.events){
5847             this.events = {};
5848         }
5849         Roo.applyIf(this.events, o);
5850     },
5851
5852     /**
5853      * Checks to see if this object has any listeners for a specified event
5854      * @param {String} eventName The name of the event to check for
5855      * @return {Boolean} True if the event is being listened for, else false
5856      */
5857     hasListener : function(eventName){
5858         var e = this.events[eventName];
5859         return typeof e == "object" && e.listeners.length > 0;
5860     }
5861 };
5862 /**
5863  * Appends an event handler to this element (shorthand for addListener)
5864  * @param {String}   eventName     The type of event to listen for
5865  * @param {Function} handler        The method the event invokes
5866  * @param {Object}   scope (optional) The scope in which to execute the handler
5867  * function. The handler function's "this" context.
5868  * @param {Object}   options  (optional)
5869  * @method
5870  */
5871 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5872 /**
5873  * Removes a listener (shorthand for removeListener)
5874  * @param {String}   eventName     The type of event to listen for
5875  * @param {Function} handler        The handler to remove
5876  * @param {Object}   scope  (optional) The scope (this object) for the handler
5877  * @method
5878  */
5879 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5880
5881 /**
5882  * Starts capture on the specified Observable. All events will be passed
5883  * to the supplied function with the event name + standard signature of the event
5884  * <b>before</b> the event is fired. If the supplied function returns false,
5885  * the event will not fire.
5886  * @param {Observable} o The Observable to capture
5887  * @param {Function} fn The function to call
5888  * @param {Object} scope (optional) The scope (this object) for the fn
5889  * @static
5890  */
5891 Roo.util.Observable.capture = function(o, fn, scope){
5892     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5893 };
5894
5895 /**
5896  * Removes <b>all</b> added captures from the Observable.
5897  * @param {Observable} o The Observable to release
5898  * @static
5899  */
5900 Roo.util.Observable.releaseCapture = function(o){
5901     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5902 };
5903
5904 (function(){
5905
5906     var createBuffered = function(h, o, scope){
5907         var task = new Roo.util.DelayedTask();
5908         return function(){
5909             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5910         };
5911     };
5912
5913     var createSingle = function(h, e, fn, scope){
5914         return function(){
5915             e.removeListener(fn, scope);
5916             return h.apply(scope, arguments);
5917         };
5918     };
5919
5920     var createDelayed = function(h, o, scope){
5921         return function(){
5922             var args = Array.prototype.slice.call(arguments, 0);
5923             setTimeout(function(){
5924                 h.apply(scope, args);
5925             }, o.delay || 10);
5926         };
5927     };
5928
5929     Roo.util.Event = function(obj, name){
5930         this.name = name;
5931         this.obj = obj;
5932         this.listeners = [];
5933     };
5934
5935     Roo.util.Event.prototype = {
5936         addListener : function(fn, scope, options){
5937             var o = options || {};
5938             scope = scope || this.obj;
5939             if(!this.isListening(fn, scope)){
5940                 var l = {fn: fn, scope: scope, options: o};
5941                 var h = fn;
5942                 if(o.delay){
5943                     h = createDelayed(h, o, scope);
5944                 }
5945                 if(o.single){
5946                     h = createSingle(h, this, fn, scope);
5947                 }
5948                 if(o.buffer){
5949                     h = createBuffered(h, o, scope);
5950                 }
5951                 l.fireFn = h;
5952                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5953                     this.listeners.push(l);
5954                 }else{
5955                     this.listeners = this.listeners.slice(0);
5956                     this.listeners.push(l);
5957                 }
5958             }
5959         },
5960
5961         findListener : function(fn, scope){
5962             scope = scope || this.obj;
5963             var ls = this.listeners;
5964             for(var i = 0, len = ls.length; i < len; i++){
5965                 var l = ls[i];
5966                 if(l.fn == fn && l.scope == scope){
5967                     return i;
5968                 }
5969             }
5970             return -1;
5971         },
5972
5973         isListening : function(fn, scope){
5974             return this.findListener(fn, scope) != -1;
5975         },
5976
5977         removeListener : function(fn, scope){
5978             var index;
5979             if((index = this.findListener(fn, scope)) != -1){
5980                 if(!this.firing){
5981                     this.listeners.splice(index, 1);
5982                 }else{
5983                     this.listeners = this.listeners.slice(0);
5984                     this.listeners.splice(index, 1);
5985                 }
5986                 return true;
5987             }
5988             return false;
5989         },
5990
5991         clearListeners : function(){
5992             this.listeners = [];
5993         },
5994
5995         fire : function(){
5996             var ls = this.listeners, scope, len = ls.length;
5997             if(len > 0){
5998                 this.firing = true;
5999                 var args = Array.prototype.slice.call(arguments, 0);
6000                 for(var i = 0; i < len; i++){
6001                     var l = ls[i];
6002                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6003                         this.firing = false;
6004                         return false;
6005                     }
6006                 }
6007                 this.firing = false;
6008             }
6009             return true;
6010         }
6011     };
6012 })();/*
6013  * Based on:
6014  * Ext JS Library 1.1.1
6015  * Copyright(c) 2006-2007, Ext JS, LLC.
6016  *
6017  * Originally Released Under LGPL - original licence link has changed is not relivant.
6018  *
6019  * Fork - LGPL
6020  * <script type="text/javascript">
6021  */
6022
6023 /**
6024  * @class Roo.EventManager
6025  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6026  * several useful events directly.
6027  * See {@link Roo.EventObject} for more details on normalized event objects.
6028  * @singleton
6029  */
6030 Roo.EventManager = function(){
6031     var docReadyEvent, docReadyProcId, docReadyState = false;
6032     var resizeEvent, resizeTask, textEvent, textSize;
6033     var E = Roo.lib.Event;
6034     var D = Roo.lib.Dom;
6035
6036
6037     var fireDocReady = function(){
6038         if(!docReadyState){
6039             docReadyState = true;
6040             Roo.isReady = true;
6041             if(docReadyProcId){
6042                 clearInterval(docReadyProcId);
6043             }
6044             if(Roo.isGecko || Roo.isOpera) {
6045                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6046             }
6047             if(Roo.isIE){
6048                 var defer = document.getElementById("ie-deferred-loader");
6049                 if(defer){
6050                     defer.onreadystatechange = null;
6051                     defer.parentNode.removeChild(defer);
6052                 }
6053             }
6054             if(docReadyEvent){
6055                 docReadyEvent.fire();
6056                 docReadyEvent.clearListeners();
6057             }
6058         }
6059     };
6060     
6061     var initDocReady = function(){
6062         docReadyEvent = new Roo.util.Event();
6063         if(Roo.isGecko || Roo.isOpera) {
6064             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6065         }else if(Roo.isIE){
6066             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6067             var defer = document.getElementById("ie-deferred-loader");
6068             defer.onreadystatechange = function(){
6069                 if(this.readyState == "complete"){
6070                     fireDocReady();
6071                 }
6072             };
6073         }else if(Roo.isSafari){ 
6074             docReadyProcId = setInterval(function(){
6075                 var rs = document.readyState;
6076                 if(rs == "complete") {
6077                     fireDocReady();     
6078                  }
6079             }, 10);
6080         }
6081         // no matter what, make sure it fires on load
6082         E.on(window, "load", fireDocReady);
6083     };
6084
6085     var createBuffered = function(h, o){
6086         var task = new Roo.util.DelayedTask(h);
6087         return function(e){
6088             // create new event object impl so new events don't wipe out properties
6089             e = new Roo.EventObjectImpl(e);
6090             task.delay(o.buffer, h, null, [e]);
6091         };
6092     };
6093
6094     var createSingle = function(h, el, ename, fn){
6095         return function(e){
6096             Roo.EventManager.removeListener(el, ename, fn);
6097             h(e);
6098         };
6099     };
6100
6101     var createDelayed = function(h, o){
6102         return function(e){
6103             // create new event object impl so new events don't wipe out properties
6104             e = new Roo.EventObjectImpl(e);
6105             setTimeout(function(){
6106                 h(e);
6107             }, o.delay || 10);
6108         };
6109     };
6110
6111     var listen = function(element, ename, opt, fn, scope){
6112         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6113         fn = fn || o.fn; scope = scope || o.scope;
6114         var el = Roo.getDom(element);
6115         if(!el){
6116             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6117         }
6118         var h = function(e){
6119             e = Roo.EventObject.setEvent(e);
6120             var t;
6121             if(o.delegate){
6122                 t = e.getTarget(o.delegate, el);
6123                 if(!t){
6124                     return;
6125                 }
6126             }else{
6127                 t = e.target;
6128             }
6129             if(o.stopEvent === true){
6130                 e.stopEvent();
6131             }
6132             if(o.preventDefault === true){
6133                e.preventDefault();
6134             }
6135             if(o.stopPropagation === true){
6136                 e.stopPropagation();
6137             }
6138
6139             if(o.normalized === false){
6140                 e = e.browserEvent;
6141             }
6142
6143             fn.call(scope || el, e, t, o);
6144         };
6145         if(o.delay){
6146             h = createDelayed(h, o);
6147         }
6148         if(o.single){
6149             h = createSingle(h, el, ename, fn);
6150         }
6151         if(o.buffer){
6152             h = createBuffered(h, o);
6153         }
6154         fn._handlers = fn._handlers || [];
6155         fn._handlers.push([Roo.id(el), ename, h]);
6156
6157         E.on(el, ename, h);
6158         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6159             el.addEventListener("DOMMouseScroll", h, false);
6160             E.on(window, 'unload', function(){
6161                 el.removeEventListener("DOMMouseScroll", h, false);
6162             });
6163         }
6164         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6165             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6166         }
6167         return h;
6168     };
6169
6170     var stopListening = function(el, ename, fn){
6171         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6172         if(hds){
6173             for(var i = 0, len = hds.length; i < len; i++){
6174                 var h = hds[i];
6175                 if(h[0] == id && h[1] == ename){
6176                     hd = h[2];
6177                     hds.splice(i, 1);
6178                     break;
6179                 }
6180             }
6181         }
6182         E.un(el, ename, hd);
6183         el = Roo.getDom(el);
6184         if(ename == "mousewheel" && el.addEventListener){
6185             el.removeEventListener("DOMMouseScroll", hd, false);
6186         }
6187         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6188             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6189         }
6190     };
6191
6192     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6193     
6194     var pub = {
6195         
6196         
6197         /** 
6198          * Fix for doc tools
6199          * @scope Roo.EventManager
6200          */
6201         
6202         
6203         /** 
6204          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6205          * object with a Roo.EventObject
6206          * @param {Function} fn        The method the event invokes
6207          * @param {Object}   scope    An object that becomes the scope of the handler
6208          * @param {boolean}  override If true, the obj passed in becomes
6209          *                             the execution scope of the listener
6210          * @return {Function} The wrapped function
6211          * @deprecated
6212          */
6213         wrap : function(fn, scope, override){
6214             return function(e){
6215                 Roo.EventObject.setEvent(e);
6216                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6217             };
6218         },
6219         
6220         /**
6221      * Appends an event handler to an element (shorthand for addListener)
6222      * @param {String/HTMLElement}   element        The html element or id to assign the
6223      * @param {String}   eventName The type of event to listen for
6224      * @param {Function} handler The method the event invokes
6225      * @param {Object}   scope (optional) The scope in which to execute the handler
6226      * function. The handler function's "this" context.
6227      * @param {Object}   options (optional) An object containing handler configuration
6228      * properties. This may contain any of the following properties:<ul>
6229      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6230      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6231      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6232      * <li>preventDefault {Boolean} True to prevent the default action</li>
6233      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6234      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6235      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6236      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6237      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6238      * by the specified number of milliseconds. If the event fires again within that time, the original
6239      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6240      * </ul><br>
6241      * <p>
6242      * <b>Combining Options</b><br>
6243      * Using the options argument, it is possible to combine different types of listeners:<br>
6244      * <br>
6245      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6246      * Code:<pre><code>
6247 el.on('click', this.onClick, this, {
6248     single: true,
6249     delay: 100,
6250     stopEvent : true,
6251     forumId: 4
6252 });</code></pre>
6253      * <p>
6254      * <b>Attaching multiple handlers in 1 call</b><br>
6255       * The method also allows for a single argument to be passed which is a config object containing properties
6256      * which specify multiple handlers.
6257      * <p>
6258      * Code:<pre><code>
6259 el.on({
6260     'click' : {
6261         fn: this.onClick
6262         scope: this,
6263         delay: 100
6264     },
6265     'mouseover' : {
6266         fn: this.onMouseOver
6267         scope: this
6268     },
6269     'mouseout' : {
6270         fn: this.onMouseOut
6271         scope: this
6272     }
6273 });</code></pre>
6274      * <p>
6275      * Or a shorthand syntax:<br>
6276      * Code:<pre><code>
6277 el.on({
6278     'click' : this.onClick,
6279     'mouseover' : this.onMouseOver,
6280     'mouseout' : this.onMouseOut
6281     scope: this
6282 });</code></pre>
6283      */
6284         addListener : function(element, eventName, fn, scope, options){
6285             if(typeof eventName == "object"){
6286                 var o = eventName;
6287                 for(var e in o){
6288                     if(propRe.test(e)){
6289                         continue;
6290                     }
6291                     if(typeof o[e] == "function"){
6292                         // shared options
6293                         listen(element, e, o, o[e], o.scope);
6294                     }else{
6295                         // individual options
6296                         listen(element, e, o[e]);
6297                     }
6298                 }
6299                 return;
6300             }
6301             return listen(element, eventName, options, fn, scope);
6302         },
6303         
6304         /**
6305          * Removes an event handler
6306          *
6307          * @param {String/HTMLElement}   element        The id or html element to remove the 
6308          *                             event from
6309          * @param {String}   eventName     The type of event
6310          * @param {Function} fn
6311          * @return {Boolean} True if a listener was actually removed
6312          */
6313         removeListener : function(element, eventName, fn){
6314             return stopListening(element, eventName, fn);
6315         },
6316         
6317         /**
6318          * Fires when the document is ready (before onload and before images are loaded). Can be 
6319          * accessed shorthanded Roo.onReady().
6320          * @param {Function} fn        The method the event invokes
6321          * @param {Object}   scope    An  object that becomes the scope of the handler
6322          * @param {boolean}  options
6323          */
6324         onDocumentReady : function(fn, scope, options){
6325             if(docReadyState){ // if it already fired
6326                 docReadyEvent.addListener(fn, scope, options);
6327                 docReadyEvent.fire();
6328                 docReadyEvent.clearListeners();
6329                 return;
6330             }
6331             if(!docReadyEvent){
6332                 initDocReady();
6333             }
6334             docReadyEvent.addListener(fn, scope, options);
6335         },
6336         
6337         /**
6338          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6339          * @param {Function} fn        The method the event invokes
6340          * @param {Object}   scope    An object that becomes the scope of the handler
6341          * @param {boolean}  options
6342          */
6343         onWindowResize : function(fn, scope, options){
6344             if(!resizeEvent){
6345                 resizeEvent = new Roo.util.Event();
6346                 resizeTask = new Roo.util.DelayedTask(function(){
6347                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6348                 });
6349                 E.on(window, "resize", function(){
6350                     if(Roo.isIE){
6351                         resizeTask.delay(50);
6352                     }else{
6353                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6354                     }
6355                 });
6356             }
6357             resizeEvent.addListener(fn, scope, options);
6358         },
6359
6360         /**
6361          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6362          * @param {Function} fn        The method the event invokes
6363          * @param {Object}   scope    An object that becomes the scope of the handler
6364          * @param {boolean}  options
6365          */
6366         onTextResize : function(fn, scope, options){
6367             if(!textEvent){
6368                 textEvent = new Roo.util.Event();
6369                 var textEl = new Roo.Element(document.createElement('div'));
6370                 textEl.dom.className = 'x-text-resize';
6371                 textEl.dom.innerHTML = 'X';
6372                 textEl.appendTo(document.body);
6373                 textSize = textEl.dom.offsetHeight;
6374                 setInterval(function(){
6375                     if(textEl.dom.offsetHeight != textSize){
6376                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6377                     }
6378                 }, this.textResizeInterval);
6379             }
6380             textEvent.addListener(fn, scope, options);
6381         },
6382
6383         /**
6384          * Removes the passed window resize listener.
6385          * @param {Function} fn        The method the event invokes
6386          * @param {Object}   scope    The scope of handler
6387          */
6388         removeResizeListener : function(fn, scope){
6389             if(resizeEvent){
6390                 resizeEvent.removeListener(fn, scope);
6391             }
6392         },
6393
6394         // private
6395         fireResize : function(){
6396             if(resizeEvent){
6397                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398             }   
6399         },
6400         /**
6401          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6402          */
6403         ieDeferSrc : false,
6404         /**
6405          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6406          */
6407         textResizeInterval : 50
6408     };
6409     
6410     /**
6411      * Fix for doc tools
6412      * @scopeAlias pub=Roo.EventManager
6413      */
6414     
6415      /**
6416      * Appends an event handler to an element (shorthand for addListener)
6417      * @param {String/HTMLElement}   element        The html element or id to assign the
6418      * @param {String}   eventName The type of event to listen for
6419      * @param {Function} handler The method the event invokes
6420      * @param {Object}   scope (optional) The scope in which to execute the handler
6421      * function. The handler function's "this" context.
6422      * @param {Object}   options (optional) An object containing handler configuration
6423      * properties. This may contain any of the following properties:<ul>
6424      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6425      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6426      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6427      * <li>preventDefault {Boolean} True to prevent the default action</li>
6428      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6429      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6430      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6431      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6432      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6433      * by the specified number of milliseconds. If the event fires again within that time, the original
6434      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6435      * </ul><br>
6436      * <p>
6437      * <b>Combining Options</b><br>
6438      * Using the options argument, it is possible to combine different types of listeners:<br>
6439      * <br>
6440      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6441      * Code:<pre><code>
6442 el.on('click', this.onClick, this, {
6443     single: true,
6444     delay: 100,
6445     stopEvent : true,
6446     forumId: 4
6447 });</code></pre>
6448      * <p>
6449      * <b>Attaching multiple handlers in 1 call</b><br>
6450       * The method also allows for a single argument to be passed which is a config object containing properties
6451      * which specify multiple handlers.
6452      * <p>
6453      * Code:<pre><code>
6454 el.on({
6455     'click' : {
6456         fn: this.onClick
6457         scope: this,
6458         delay: 100
6459     },
6460     'mouseover' : {
6461         fn: this.onMouseOver
6462         scope: this
6463     },
6464     'mouseout' : {
6465         fn: this.onMouseOut
6466         scope: this
6467     }
6468 });</code></pre>
6469      * <p>
6470      * Or a shorthand syntax:<br>
6471      * Code:<pre><code>
6472 el.on({
6473     'click' : this.onClick,
6474     'mouseover' : this.onMouseOver,
6475     'mouseout' : this.onMouseOut
6476     scope: this
6477 });</code></pre>
6478      */
6479     pub.on = pub.addListener;
6480     pub.un = pub.removeListener;
6481
6482     pub.stoppedMouseDownEvent = new Roo.util.Event();
6483     return pub;
6484 }();
6485 /**
6486   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6487   * @param {Function} fn        The method the event invokes
6488   * @param {Object}   scope    An  object that becomes the scope of the handler
6489   * @param {boolean}  override If true, the obj passed in becomes
6490   *                             the execution scope of the listener
6491   * @member Roo
6492   * @method onReady
6493  */
6494 Roo.onReady = Roo.EventManager.onDocumentReady;
6495
6496 Roo.onReady(function(){
6497     var bd = Roo.get(document.body);
6498     if(!bd){ return; }
6499
6500     var cls = [
6501             Roo.isIE ? "roo-ie"
6502             : Roo.isGecko ? "roo-gecko"
6503             : Roo.isOpera ? "roo-opera"
6504             : Roo.isSafari ? "roo-safari" : ""];
6505
6506     if(Roo.isMac){
6507         cls.push("roo-mac");
6508     }
6509     if(Roo.isLinux){
6510         cls.push("roo-linux");
6511     }
6512     if(Roo.isBorderBox){
6513         cls.push('roo-border-box');
6514     }
6515     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6516         var p = bd.dom.parentNode;
6517         if(p){
6518             p.className += ' roo-strict';
6519         }
6520     }
6521     bd.addClass(cls.join(' '));
6522 });
6523
6524 /**
6525  * @class Roo.EventObject
6526  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6527  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6528  * Example:
6529  * <pre><code>
6530  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6531     e.preventDefault();
6532     var target = e.getTarget();
6533     ...
6534  }
6535  var myDiv = Roo.get("myDiv");
6536  myDiv.on("click", handleClick);
6537  //or
6538  Roo.EventManager.on("myDiv", 'click', handleClick);
6539  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6540  </code></pre>
6541  * @singleton
6542  */
6543 Roo.EventObject = function(){
6544     
6545     var E = Roo.lib.Event;
6546     
6547     // safari keypress events for special keys return bad keycodes
6548     var safariKeys = {
6549         63234 : 37, // left
6550         63235 : 39, // right
6551         63232 : 38, // up
6552         63233 : 40, // down
6553         63276 : 33, // page up
6554         63277 : 34, // page down
6555         63272 : 46, // delete
6556         63273 : 36, // home
6557         63275 : 35  // end
6558     };
6559
6560     // normalize button clicks
6561     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6562                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6563
6564     Roo.EventObjectImpl = function(e){
6565         if(e){
6566             this.setEvent(e.browserEvent || e);
6567         }
6568     };
6569     Roo.EventObjectImpl.prototype = {
6570         /**
6571          * Used to fix doc tools.
6572          * @scope Roo.EventObject.prototype
6573          */
6574             
6575
6576         
6577         
6578         /** The normal browser event */
6579         browserEvent : null,
6580         /** The button pressed in a mouse event */
6581         button : -1,
6582         /** True if the shift key was down during the event */
6583         shiftKey : false,
6584         /** True if the control key was down during the event */
6585         ctrlKey : false,
6586         /** True if the alt key was down during the event */
6587         altKey : false,
6588
6589         /** Key constant 
6590         * @type Number */
6591         BACKSPACE : 8,
6592         /** Key constant 
6593         * @type Number */
6594         TAB : 9,
6595         /** Key constant 
6596         * @type Number */
6597         RETURN : 13,
6598         /** Key constant 
6599         * @type Number */
6600         ENTER : 13,
6601         /** Key constant 
6602         * @type Number */
6603         SHIFT : 16,
6604         /** Key constant 
6605         * @type Number */
6606         CONTROL : 17,
6607         /** Key constant 
6608         * @type Number */
6609         ESC : 27,
6610         /** Key constant 
6611         * @type Number */
6612         SPACE : 32,
6613         /** Key constant 
6614         * @type Number */
6615         PAGEUP : 33,
6616         /** Key constant 
6617         * @type Number */
6618         PAGEDOWN : 34,
6619         /** Key constant 
6620         * @type Number */
6621         END : 35,
6622         /** Key constant 
6623         * @type Number */
6624         HOME : 36,
6625         /** Key constant 
6626         * @type Number */
6627         LEFT : 37,
6628         /** Key constant 
6629         * @type Number */
6630         UP : 38,
6631         /** Key constant 
6632         * @type Number */
6633         RIGHT : 39,
6634         /** Key constant 
6635         * @type Number */
6636         DOWN : 40,
6637         /** Key constant 
6638         * @type Number */
6639         DELETE : 46,
6640         /** Key constant 
6641         * @type Number */
6642         F5 : 116,
6643
6644            /** @private */
6645         setEvent : function(e){
6646             if(e == this || (e && e.browserEvent)){ // already wrapped
6647                 return e;
6648             }
6649             this.browserEvent = e;
6650             if(e){
6651                 // normalize buttons
6652                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6653                 if(e.type == 'click' && this.button == -1){
6654                     this.button = 0;
6655                 }
6656                 this.type = e.type;
6657                 this.shiftKey = e.shiftKey;
6658                 // mac metaKey behaves like ctrlKey
6659                 this.ctrlKey = e.ctrlKey || e.metaKey;
6660                 this.altKey = e.altKey;
6661                 // in getKey these will be normalized for the mac
6662                 this.keyCode = e.keyCode;
6663                 // keyup warnings on firefox.
6664                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6665                 // cache the target for the delayed and or buffered events
6666                 this.target = E.getTarget(e);
6667                 // same for XY
6668                 this.xy = E.getXY(e);
6669             }else{
6670                 this.button = -1;
6671                 this.shiftKey = false;
6672                 this.ctrlKey = false;
6673                 this.altKey = false;
6674                 this.keyCode = 0;
6675                 this.charCode =0;
6676                 this.target = null;
6677                 this.xy = [0, 0];
6678             }
6679             return this;
6680         },
6681
6682         /**
6683          * Stop the event (preventDefault and stopPropagation)
6684          */
6685         stopEvent : function(){
6686             if(this.browserEvent){
6687                 if(this.browserEvent.type == 'mousedown'){
6688                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6689                 }
6690                 E.stopEvent(this.browserEvent);
6691             }
6692         },
6693
6694         /**
6695          * Prevents the browsers default handling of the event.
6696          */
6697         preventDefault : function(){
6698             if(this.browserEvent){
6699                 E.preventDefault(this.browserEvent);
6700             }
6701         },
6702
6703         /** @private */
6704         isNavKeyPress : function(){
6705             var k = this.keyCode;
6706             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6707             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6708         },
6709
6710         isSpecialKey : function(){
6711             var k = this.keyCode;
6712             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6713             (k == 16) || (k == 17) ||
6714             (k >= 18 && k <= 20) ||
6715             (k >= 33 && k <= 35) ||
6716             (k >= 36 && k <= 39) ||
6717             (k >= 44 && k <= 45);
6718         },
6719         /**
6720          * Cancels bubbling of the event.
6721          */
6722         stopPropagation : function(){
6723             if(this.browserEvent){
6724                 if(this.type == 'mousedown'){
6725                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6726                 }
6727                 E.stopPropagation(this.browserEvent);
6728             }
6729         },
6730
6731         /**
6732          * Gets the key code for the event.
6733          * @return {Number}
6734          */
6735         getCharCode : function(){
6736             return this.charCode || this.keyCode;
6737         },
6738
6739         /**
6740          * Returns a normalized keyCode for the event.
6741          * @return {Number} The key code
6742          */
6743         getKey : function(){
6744             var k = this.keyCode || this.charCode;
6745             return Roo.isSafari ? (safariKeys[k] || k) : k;
6746         },
6747
6748         /**
6749          * Gets the x coordinate of the event.
6750          * @return {Number}
6751          */
6752         getPageX : function(){
6753             return this.xy[0];
6754         },
6755
6756         /**
6757          * Gets the y coordinate of the event.
6758          * @return {Number}
6759          */
6760         getPageY : function(){
6761             return this.xy[1];
6762         },
6763
6764         /**
6765          * Gets the time of the event.
6766          * @return {Number}
6767          */
6768         getTime : function(){
6769             if(this.browserEvent){
6770                 return E.getTime(this.browserEvent);
6771             }
6772             return null;
6773         },
6774
6775         /**
6776          * Gets the page coordinates of the event.
6777          * @return {Array} The xy values like [x, y]
6778          */
6779         getXY : function(){
6780             return this.xy;
6781         },
6782
6783         /**
6784          * Gets the target for the event.
6785          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6786          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6787                 search as a number or element (defaults to 10 || document.body)
6788          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6789          * @return {HTMLelement}
6790          */
6791         getTarget : function(selector, maxDepth, returnEl){
6792             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6793         },
6794         /**
6795          * Gets the related target.
6796          * @return {HTMLElement}
6797          */
6798         getRelatedTarget : function(){
6799             if(this.browserEvent){
6800                 return E.getRelatedTarget(this.browserEvent);
6801             }
6802             return null;
6803         },
6804
6805         /**
6806          * Normalizes mouse wheel delta across browsers
6807          * @return {Number} The delta
6808          */
6809         getWheelDelta : function(){
6810             var e = this.browserEvent;
6811             var delta = 0;
6812             if(e.wheelDelta){ /* IE/Opera. */
6813                 delta = e.wheelDelta/120;
6814             }else if(e.detail){ /* Mozilla case. */
6815                 delta = -e.detail/3;
6816             }
6817             return delta;
6818         },
6819
6820         /**
6821          * Returns true if the control, meta, shift or alt key was pressed during this event.
6822          * @return {Boolean}
6823          */
6824         hasModifier : function(){
6825             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6826         },
6827
6828         /**
6829          * Returns true if the target of this event equals el or is a child of el
6830          * @param {String/HTMLElement/Element} el
6831          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6832          * @return {Boolean}
6833          */
6834         within : function(el, related){
6835             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6836             return t && Roo.fly(el).contains(t);
6837         },
6838
6839         getPoint : function(){
6840             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6841         }
6842     };
6843
6844     return new Roo.EventObjectImpl();
6845 }();
6846             
6847     /*
6848  * Based on:
6849  * Ext JS Library 1.1.1
6850  * Copyright(c) 2006-2007, Ext JS, LLC.
6851  *
6852  * Originally Released Under LGPL - original licence link has changed is not relivant.
6853  *
6854  * Fork - LGPL
6855  * <script type="text/javascript">
6856  */
6857
6858  
6859 // was in Composite Element!??!?!
6860  
6861 (function(){
6862     var D = Roo.lib.Dom;
6863     var E = Roo.lib.Event;
6864     var A = Roo.lib.Anim;
6865
6866     // local style camelizing for speed
6867     var propCache = {};
6868     var camelRe = /(-[a-z])/gi;
6869     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6870     var view = document.defaultView;
6871
6872 /**
6873  * @class Roo.Element
6874  * Represents an Element in the DOM.<br><br>
6875  * Usage:<br>
6876 <pre><code>
6877 var el = Roo.get("my-div");
6878
6879 // or with getEl
6880 var el = getEl("my-div");
6881
6882 // or with a DOM element
6883 var el = Roo.get(myDivElement);
6884 </code></pre>
6885  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6886  * each call instead of constructing a new one.<br><br>
6887  * <b>Animations</b><br />
6888  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6889  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6890 <pre>
6891 Option    Default   Description
6892 --------- --------  ---------------------------------------------
6893 duration  .35       The duration of the animation in seconds
6894 easing    easeOut   The YUI easing method
6895 callback  none      A function to execute when the anim completes
6896 scope     this      The scope (this) of the callback function
6897 </pre>
6898 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6899 * manipulate the animation. Here's an example:
6900 <pre><code>
6901 var el = Roo.get("my-div");
6902
6903 // no animation
6904 el.setWidth(100);
6905
6906 // default animation
6907 el.setWidth(100, true);
6908
6909 // animation with some options set
6910 el.setWidth(100, {
6911     duration: 1,
6912     callback: this.foo,
6913     scope: this
6914 });
6915
6916 // using the "anim" property to get the Anim object
6917 var opt = {
6918     duration: 1,
6919     callback: this.foo,
6920     scope: this
6921 };
6922 el.setWidth(100, opt);
6923 ...
6924 if(opt.anim.isAnimated()){
6925     opt.anim.stop();
6926 }
6927 </code></pre>
6928 * <b> Composite (Collections of) Elements</b><br />
6929  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6930  * @constructor Create a new Element directly.
6931  * @param {String/HTMLElement} element
6932  * @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).
6933  */
6934     Roo.Element = function(element, forceNew){
6935         var dom = typeof element == "string" ?
6936                 document.getElementById(element) : element;
6937         if(!dom){ // invalid id/element
6938             return null;
6939         }
6940         var id = dom.id;
6941         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6942             return Roo.Element.cache[id];
6943         }
6944
6945         /**
6946          * The DOM element
6947          * @type HTMLElement
6948          */
6949         this.dom = dom;
6950
6951         /**
6952          * The DOM element ID
6953          * @type String
6954          */
6955         this.id = id || Roo.id(dom);
6956     };
6957
6958     var El = Roo.Element;
6959
6960     El.prototype = {
6961         /**
6962          * The element's default display mode  (defaults to "")
6963          * @type String
6964          */
6965         originalDisplay : "",
6966
6967         visibilityMode : 1,
6968         /**
6969          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6970          * @type String
6971          */
6972         defaultUnit : "px",
6973         /**
6974          * Sets the element's visibility mode. When setVisible() is called it
6975          * will use this to determine whether to set the visibility or the display property.
6976          * @param visMode Element.VISIBILITY or Element.DISPLAY
6977          * @return {Roo.Element} this
6978          */
6979         setVisibilityMode : function(visMode){
6980             this.visibilityMode = visMode;
6981             return this;
6982         },
6983         /**
6984          * Convenience method for setVisibilityMode(Element.DISPLAY)
6985          * @param {String} display (optional) What to set display to when visible
6986          * @return {Roo.Element} this
6987          */
6988         enableDisplayMode : function(display){
6989             this.setVisibilityMode(El.DISPLAY);
6990             if(typeof display != "undefined") this.originalDisplay = display;
6991             return this;
6992         },
6993
6994         /**
6995          * 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)
6996          * @param {String} selector The simple selector to test
6997          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6998                 search as a number or element (defaults to 10 || document.body)
6999          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7000          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7001          */
7002         findParent : function(simpleSelector, maxDepth, returnEl){
7003             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7004             maxDepth = maxDepth || 50;
7005             if(typeof maxDepth != "number"){
7006                 stopEl = Roo.getDom(maxDepth);
7007                 maxDepth = 10;
7008             }
7009             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7010                 if(dq.is(p, simpleSelector)){
7011                     return returnEl ? Roo.get(p) : p;
7012                 }
7013                 depth++;
7014                 p = p.parentNode;
7015             }
7016             return null;
7017         },
7018
7019
7020         /**
7021          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7022          * @param {String} selector The simple selector to test
7023          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7024                 search as a number or element (defaults to 10 || document.body)
7025          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7026          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7027          */
7028         findParentNode : function(simpleSelector, maxDepth, returnEl){
7029             var p = Roo.fly(this.dom.parentNode, '_internal');
7030             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7031         },
7032
7033         /**
7034          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7035          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7036          * @param {String} selector The simple selector to test
7037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7038                 search as a number or element (defaults to 10 || document.body)
7039          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7040          */
7041         up : function(simpleSelector, maxDepth){
7042             return this.findParentNode(simpleSelector, maxDepth, true);
7043         },
7044
7045
7046
7047         /**
7048          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7049          * @param {String} selector The simple selector to test
7050          * @return {Boolean} True if this element matches the selector, else false
7051          */
7052         is : function(simpleSelector){
7053             return Roo.DomQuery.is(this.dom, simpleSelector);
7054         },
7055
7056         /**
7057          * Perform animation on this element.
7058          * @param {Object} args The YUI animation control args
7059          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7060          * @param {Function} onComplete (optional) Function to call when animation completes
7061          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7062          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7063          * @return {Roo.Element} this
7064          */
7065         animate : function(args, duration, onComplete, easing, animType){
7066             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7067             return this;
7068         },
7069
7070         /*
7071          * @private Internal animation call
7072          */
7073         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7074             animType = animType || 'run';
7075             opt = opt || {};
7076             var anim = Roo.lib.Anim[animType](
7077                 this.dom, args,
7078                 (opt.duration || defaultDur) || .35,
7079                 (opt.easing || defaultEase) || 'easeOut',
7080                 function(){
7081                     Roo.callback(cb, this);
7082                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7083                 },
7084                 this
7085             );
7086             opt.anim = anim;
7087             return anim;
7088         },
7089
7090         // private legacy anim prep
7091         preanim : function(a, i){
7092             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7093         },
7094
7095         /**
7096          * Removes worthless text nodes
7097          * @param {Boolean} forceReclean (optional) By default the element
7098          * keeps track if it has been cleaned already so
7099          * you can call this over and over. However, if you update the element and
7100          * need to force a reclean, you can pass true.
7101          */
7102         clean : function(forceReclean){
7103             if(this.isCleaned && forceReclean !== true){
7104                 return this;
7105             }
7106             var ns = /\S/;
7107             var d = this.dom, n = d.firstChild, ni = -1;
7108             while(n){
7109                 var nx = n.nextSibling;
7110                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7111                     d.removeChild(n);
7112                 }else{
7113                     n.nodeIndex = ++ni;
7114                 }
7115                 n = nx;
7116             }
7117             this.isCleaned = true;
7118             return this;
7119         },
7120
7121         // private
7122         calcOffsetsTo : function(el){
7123             el = Roo.get(el);
7124             var d = el.dom;
7125             var restorePos = false;
7126             if(el.getStyle('position') == 'static'){
7127                 el.position('relative');
7128                 restorePos = true;
7129             }
7130             var x = 0, y =0;
7131             var op = this.dom;
7132             while(op && op != d && op.tagName != 'HTML'){
7133                 x+= op.offsetLeft;
7134                 y+= op.offsetTop;
7135                 op = op.offsetParent;
7136             }
7137             if(restorePos){
7138                 el.position('static');
7139             }
7140             return [x, y];
7141         },
7142
7143         /**
7144          * Scrolls this element into view within the passed container.
7145          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7146          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7147          * @return {Roo.Element} this
7148          */
7149         scrollIntoView : function(container, hscroll){
7150             var c = Roo.getDom(container) || document.body;
7151             var el = this.dom;
7152
7153             var o = this.calcOffsetsTo(c),
7154                 l = o[0],
7155                 t = o[1],
7156                 b = t+el.offsetHeight,
7157                 r = l+el.offsetWidth;
7158
7159             var ch = c.clientHeight;
7160             var ct = parseInt(c.scrollTop, 10);
7161             var cl = parseInt(c.scrollLeft, 10);
7162             var cb = ct + ch;
7163             var cr = cl + c.clientWidth;
7164
7165             if(t < ct){
7166                 c.scrollTop = t;
7167             }else if(b > cb){
7168                 c.scrollTop = b-ch;
7169             }
7170
7171             if(hscroll !== false){
7172                 if(l < cl){
7173                     c.scrollLeft = l;
7174                 }else if(r > cr){
7175                     c.scrollLeft = r-c.clientWidth;
7176                 }
7177             }
7178             return this;
7179         },
7180
7181         // private
7182         scrollChildIntoView : function(child, hscroll){
7183             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7184         },
7185
7186         /**
7187          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7188          * the new height may not be available immediately.
7189          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7190          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7191          * @param {Function} onComplete (optional) Function to call when animation completes
7192          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7193          * @return {Roo.Element} this
7194          */
7195         autoHeight : function(animate, duration, onComplete, easing){
7196             var oldHeight = this.getHeight();
7197             this.clip();
7198             this.setHeight(1); // force clipping
7199             setTimeout(function(){
7200                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7201                 if(!animate){
7202                     this.setHeight(height);
7203                     this.unclip();
7204                     if(typeof onComplete == "function"){
7205                         onComplete();
7206                     }
7207                 }else{
7208                     this.setHeight(oldHeight); // restore original height
7209                     this.setHeight(height, animate, duration, function(){
7210                         this.unclip();
7211                         if(typeof onComplete == "function") onComplete();
7212                     }.createDelegate(this), easing);
7213                 }
7214             }.createDelegate(this), 0);
7215             return this;
7216         },
7217
7218         /**
7219          * Returns true if this element is an ancestor of the passed element
7220          * @param {HTMLElement/String} el The element to check
7221          * @return {Boolean} True if this element is an ancestor of el, else false
7222          */
7223         contains : function(el){
7224             if(!el){return false;}
7225             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7226         },
7227
7228         /**
7229          * Checks whether the element is currently visible using both visibility and display properties.
7230          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7231          * @return {Boolean} True if the element is currently visible, else false
7232          */
7233         isVisible : function(deep) {
7234             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7235             if(deep !== true || !vis){
7236                 return vis;
7237             }
7238             var p = this.dom.parentNode;
7239             while(p && p.tagName.toLowerCase() != "body"){
7240                 if(!Roo.fly(p, '_isVisible').isVisible()){
7241                     return false;
7242                 }
7243                 p = p.parentNode;
7244             }
7245             return true;
7246         },
7247
7248         /**
7249          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7250          * @param {String} selector The CSS selector
7251          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7252          * @return {CompositeElement/CompositeElementLite} The composite element
7253          */
7254         select : function(selector, unique){
7255             return El.select(selector, unique, this.dom);
7256         },
7257
7258         /**
7259          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7260          * @param {String} selector The CSS selector
7261          * @return {Array} An array of the matched nodes
7262          */
7263         query : function(selector, unique){
7264             return Roo.DomQuery.select(selector, this.dom);
7265         },
7266
7267         /**
7268          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7269          * @param {String} selector The CSS selector
7270          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7271          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7272          */
7273         child : function(selector, returnDom){
7274             var n = Roo.DomQuery.selectNode(selector, this.dom);
7275             return returnDom ? n : Roo.get(n);
7276         },
7277
7278         /**
7279          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         down : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7291          * @param {String} group The group the DD object is member of
7292          * @param {Object} config The DD config object
7293          * @param {Object} overrides An object containing methods to override/implement on the DD object
7294          * @return {Roo.dd.DD} The DD object
7295          */
7296         initDD : function(group, config, overrides){
7297             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7298             return Roo.apply(dd, overrides);
7299         },
7300
7301         /**
7302          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7303          * @param {String} group The group the DDProxy object is member of
7304          * @param {Object} config The DDProxy config object
7305          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7306          * @return {Roo.dd.DDProxy} The DDProxy object
7307          */
7308         initDDProxy : function(group, config, overrides){
7309             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7310             return Roo.apply(dd, overrides);
7311         },
7312
7313         /**
7314          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7315          * @param {String} group The group the DDTarget object is member of
7316          * @param {Object} config The DDTarget config object
7317          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7318          * @return {Roo.dd.DDTarget} The DDTarget object
7319          */
7320         initDDTarget : function(group, config, overrides){
7321             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7322             return Roo.apply(dd, overrides);
7323         },
7324
7325         /**
7326          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7327          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7328          * @param {Boolean} visible Whether the element is visible
7329          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7330          * @return {Roo.Element} this
7331          */
7332          setVisible : function(visible, animate){
7333             if(!animate || !A){
7334                 if(this.visibilityMode == El.DISPLAY){
7335                     this.setDisplayed(visible);
7336                 }else{
7337                     this.fixDisplay();
7338                     this.dom.style.visibility = visible ? "visible" : "hidden";
7339                 }
7340             }else{
7341                 // closure for composites
7342                 var dom = this.dom;
7343                 var visMode = this.visibilityMode;
7344                 if(visible){
7345                     this.setOpacity(.01);
7346                     this.setVisible(true);
7347                 }
7348                 this.anim({opacity: { to: (visible?1:0) }},
7349                       this.preanim(arguments, 1),
7350                       null, .35, 'easeIn', function(){
7351                          if(!visible){
7352                              if(visMode == El.DISPLAY){
7353                                  dom.style.display = "none";
7354                              }else{
7355                                  dom.style.visibility = "hidden";
7356                              }
7357                              Roo.get(dom).setOpacity(1);
7358                          }
7359                      });
7360             }
7361             return this;
7362         },
7363
7364         /**
7365          * Returns true if display is not "none"
7366          * @return {Boolean}
7367          */
7368         isDisplayed : function() {
7369             return this.getStyle("display") != "none";
7370         },
7371
7372         /**
7373          * Toggles the element's visibility or display, depending on visibility mode.
7374          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7375          * @return {Roo.Element} this
7376          */
7377         toggle : function(animate){
7378             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7379             return this;
7380         },
7381
7382         /**
7383          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7384          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7385          * @return {Roo.Element} this
7386          */
7387         setDisplayed : function(value) {
7388             if(typeof value == "boolean"){
7389                value = value ? this.originalDisplay : "none";
7390             }
7391             this.setStyle("display", value);
7392             return this;
7393         },
7394
7395         /**
7396          * Tries to focus the element. Any exceptions are caught and ignored.
7397          * @return {Roo.Element} this
7398          */
7399         focus : function() {
7400             try{
7401                 this.dom.focus();
7402             }catch(e){}
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to blur the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         blur : function() {
7411             try{
7412                 this.dom.blur();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7419          * @param {String/Array} className The CSS class to add, or an array of classes
7420          * @return {Roo.Element} this
7421          */
7422         addClass : function(className){
7423             if(className instanceof Array){
7424                 for(var i = 0, len = className.length; i < len; i++) {
7425                     this.addClass(className[i]);
7426                 }
7427             }else{
7428                 if(className && !this.hasClass(className)){
7429                     this.dom.className = this.dom.className + " " + className;
7430                 }
7431             }
7432             return this;
7433         },
7434
7435         /**
7436          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7437          * @param {String/Array} className The CSS class to add, or an array of classes
7438          * @return {Roo.Element} this
7439          */
7440         radioClass : function(className){
7441             var siblings = this.dom.parentNode.childNodes;
7442             for(var i = 0; i < siblings.length; i++) {
7443                 var s = siblings[i];
7444                 if(s.nodeType == 1){
7445                     Roo.get(s).removeClass(className);
7446                 }
7447             }
7448             this.addClass(className);
7449             return this;
7450         },
7451
7452         /**
7453          * Removes one or more CSS classes from the element.
7454          * @param {String/Array} className The CSS class to remove, or an array of classes
7455          * @return {Roo.Element} this
7456          */
7457         removeClass : function(className){
7458             if(!className || !this.dom.className){
7459                 return this;
7460             }
7461             if(className instanceof Array){
7462                 for(var i = 0, len = className.length; i < len; i++) {
7463                     this.removeClass(className[i]);
7464                 }
7465             }else{
7466                 if(this.hasClass(className)){
7467                     var re = this.classReCache[className];
7468                     if (!re) {
7469                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7470                        this.classReCache[className] = re;
7471                     }
7472                     this.dom.className =
7473                         this.dom.className.replace(re, " ");
7474                 }
7475             }
7476             return this;
7477         },
7478
7479         // private
7480         classReCache: {},
7481
7482         /**
7483          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7484          * @param {String} className The CSS class to toggle
7485          * @return {Roo.Element} this
7486          */
7487         toggleClass : function(className){
7488             if(this.hasClass(className)){
7489                 this.removeClass(className);
7490             }else{
7491                 this.addClass(className);
7492             }
7493             return this;
7494         },
7495
7496         /**
7497          * Checks if the specified CSS class exists on this element's DOM node.
7498          * @param {String} className The CSS class to check for
7499          * @return {Boolean} True if the class exists, else false
7500          */
7501         hasClass : function(className){
7502             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7503         },
7504
7505         /**
7506          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7507          * @param {String} oldClassName The CSS class to replace
7508          * @param {String} newClassName The replacement CSS class
7509          * @return {Roo.Element} this
7510          */
7511         replaceClass : function(oldClassName, newClassName){
7512             this.removeClass(oldClassName);
7513             this.addClass(newClassName);
7514             return this;
7515         },
7516
7517         /**
7518          * Returns an object with properties matching the styles requested.
7519          * For example, el.getStyles('color', 'font-size', 'width') might return
7520          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7521          * @param {String} style1 A style name
7522          * @param {String} style2 A style name
7523          * @param {String} etc.
7524          * @return {Object} The style object
7525          */
7526         getStyles : function(){
7527             var a = arguments, len = a.length, r = {};
7528             for(var i = 0; i < len; i++){
7529                 r[a[i]] = this.getStyle(a[i]);
7530             }
7531             return r;
7532         },
7533
7534         /**
7535          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7536          * @param {String} property The style property whose value is returned.
7537          * @return {String} The current value of the style property for this element.
7538          */
7539         getStyle : function(){
7540             return view && view.getComputedStyle ?
7541                 function(prop){
7542                     var el = this.dom, v, cs, camel;
7543                     if(prop == 'float'){
7544                         prop = "cssFloat";
7545                     }
7546                     if(el.style && (v = el.style[prop])){
7547                         return v;
7548                     }
7549                     if(cs = view.getComputedStyle(el, "")){
7550                         if(!(camel = propCache[prop])){
7551                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7552                         }
7553                         return cs[camel];
7554                     }
7555                     return null;
7556                 } :
7557                 function(prop){
7558                     var el = this.dom, v, cs, camel;
7559                     if(prop == 'opacity'){
7560                         if(typeof el.style.filter == 'string'){
7561                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7562                             if(m){
7563                                 var fv = parseFloat(m[1]);
7564                                 if(!isNaN(fv)){
7565                                     return fv ? fv / 100 : 0;
7566                                 }
7567                             }
7568                         }
7569                         return 1;
7570                     }else if(prop == 'float'){
7571                         prop = "styleFloat";
7572                     }
7573                     if(!(camel = propCache[prop])){
7574                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7575                     }
7576                     if(v = el.style[camel]){
7577                         return v;
7578                     }
7579                     if(cs = el.currentStyle){
7580                         return cs[camel];
7581                     }
7582                     return null;
7583                 };
7584         }(),
7585
7586         /**
7587          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7588          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7589          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7590          * @return {Roo.Element} this
7591          */
7592         setStyle : function(prop, value){
7593             if(typeof prop == "string"){
7594                 
7595                 if (prop == 'float') {
7596                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7597                     return this;
7598                 }
7599                 
7600                 var camel;
7601                 if(!(camel = propCache[prop])){
7602                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7603                 }
7604                 
7605                 if(camel == 'opacity') {
7606                     this.setOpacity(value);
7607                 }else{
7608                     this.dom.style[camel] = value;
7609                 }
7610             }else{
7611                 for(var style in prop){
7612                     if(typeof prop[style] != "function"){
7613                        this.setStyle(style, prop[style]);
7614                     }
7615                 }
7616             }
7617             return this;
7618         },
7619
7620         /**
7621          * More flexible version of {@link #setStyle} for setting style properties.
7622          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7623          * a function which returns such a specification.
7624          * @return {Roo.Element} this
7625          */
7626         applyStyles : function(style){
7627             Roo.DomHelper.applyStyles(this.dom, style);
7628             return this;
7629         },
7630
7631         /**
7632           * 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).
7633           * @return {Number} The X position of the element
7634           */
7635         getX : function(){
7636             return D.getX(this.dom);
7637         },
7638
7639         /**
7640           * 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).
7641           * @return {Number} The Y position of the element
7642           */
7643         getY : function(){
7644             return D.getY(this.dom);
7645         },
7646
7647         /**
7648           * 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).
7649           * @return {Array} The XY position of the element
7650           */
7651         getXY : function(){
7652             return D.getXY(this.dom);
7653         },
7654
7655         /**
7656          * 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).
7657          * @param {Number} The X position of the element
7658          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7659          * @return {Roo.Element} this
7660          */
7661         setX : function(x, animate){
7662             if(!animate || !A){
7663                 D.setX(this.dom, x);
7664             }else{
7665                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * 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).
7672          * @param {Number} The Y position of the element
7673          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7674          * @return {Roo.Element} this
7675          */
7676         setY : function(y, animate){
7677             if(!animate || !A){
7678                 D.setY(this.dom, y);
7679             }else{
7680                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7687          * @param {String} left The left CSS property value
7688          * @return {Roo.Element} this
7689          */
7690         setLeft : function(left){
7691             this.setStyle("left", this.addUnits(left));
7692             return this;
7693         },
7694
7695         /**
7696          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7697          * @param {String} top The top CSS property value
7698          * @return {Roo.Element} this
7699          */
7700         setTop : function(top){
7701             this.setStyle("top", this.addUnits(top));
7702             return this;
7703         },
7704
7705         /**
7706          * Sets the element's CSS right style.
7707          * @param {String} right The right CSS property value
7708          * @return {Roo.Element} this
7709          */
7710         setRight : function(right){
7711             this.setStyle("right", this.addUnits(right));
7712             return this;
7713         },
7714
7715         /**
7716          * Sets the element's CSS bottom style.
7717          * @param {String} bottom The bottom CSS property value
7718          * @return {Roo.Element} this
7719          */
7720         setBottom : function(bottom){
7721             this.setStyle("bottom", this.addUnits(bottom));
7722             return this;
7723         },
7724
7725         /**
7726          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7727          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7728          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7729          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7730          * @return {Roo.Element} this
7731          */
7732         setXY : function(pos, animate){
7733             if(!animate || !A){
7734                 D.setXY(this.dom, pos);
7735             }else{
7736                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7737             }
7738             return this;
7739         },
7740
7741         /**
7742          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7743          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7744          * @param {Number} x X value for new position (coordinates are page-based)
7745          * @param {Number} y Y value for new position (coordinates are page-based)
7746          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7747          * @return {Roo.Element} this
7748          */
7749         setLocation : function(x, y, animate){
7750             this.setXY([x, y], this.preanim(arguments, 2));
7751             return this;
7752         },
7753
7754         /**
7755          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7756          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7757          * @param {Number} x X value for new position (coordinates are page-based)
7758          * @param {Number} y Y value for new position (coordinates are page-based)
7759          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7760          * @return {Roo.Element} this
7761          */
7762         moveTo : function(x, y, animate){
7763             this.setXY([x, y], this.preanim(arguments, 2));
7764             return this;
7765         },
7766
7767         /**
7768          * Returns the region of the given element.
7769          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7770          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7771          */
7772         getRegion : function(){
7773             return D.getRegion(this.dom);
7774         },
7775
7776         /**
7777          * Returns the offset height of the element
7778          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7779          * @return {Number} The element's height
7780          */
7781         getHeight : function(contentHeight){
7782             var h = this.dom.offsetHeight || 0;
7783             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7784         },
7785
7786         /**
7787          * Returns the offset width of the element
7788          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7789          * @return {Number} The element's width
7790          */
7791         getWidth : function(contentWidth){
7792             var w = this.dom.offsetWidth || 0;
7793             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7794         },
7795
7796         /**
7797          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7798          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7799          * if a height has not been set using CSS.
7800          * @return {Number}
7801          */
7802         getComputedHeight : function(){
7803             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7804             if(!h){
7805                 h = parseInt(this.getStyle('height'), 10) || 0;
7806                 if(!this.isBorderBox()){
7807                     h += this.getFrameWidth('tb');
7808                 }
7809             }
7810             return h;
7811         },
7812
7813         /**
7814          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7815          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7816          * if a width has not been set using CSS.
7817          * @return {Number}
7818          */
7819         getComputedWidth : function(){
7820             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7821             if(!w){
7822                 w = parseInt(this.getStyle('width'), 10) || 0;
7823                 if(!this.isBorderBox()){
7824                     w += this.getFrameWidth('lr');
7825                 }
7826             }
7827             return w;
7828         },
7829
7830         /**
7831          * Returns the size of the element.
7832          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7833          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7834          */
7835         getSize : function(contentSize){
7836             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7837         },
7838
7839         /**
7840          * Returns the width and height of the viewport.
7841          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7842          */
7843         getViewSize : function(){
7844             var d = this.dom, doc = document, aw = 0, ah = 0;
7845             if(d == doc || d == doc.body){
7846                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7847             }else{
7848                 return {
7849                     width : d.clientWidth,
7850                     height: d.clientHeight
7851                 };
7852             }
7853         },
7854
7855         /**
7856          * Returns the value of the "value" attribute
7857          * @param {Boolean} asNumber true to parse the value as a number
7858          * @return {String/Number}
7859          */
7860         getValue : function(asNumber){
7861             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7862         },
7863
7864         // private
7865         adjustWidth : function(width){
7866             if(typeof width == "number"){
7867                 if(this.autoBoxAdjust && !this.isBorderBox()){
7868                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7869                 }
7870                 if(width < 0){
7871                     width = 0;
7872                 }
7873             }
7874             return width;
7875         },
7876
7877         // private
7878         adjustHeight : function(height){
7879             if(typeof height == "number"){
7880                if(this.autoBoxAdjust && !this.isBorderBox()){
7881                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7882                }
7883                if(height < 0){
7884                    height = 0;
7885                }
7886             }
7887             return height;
7888         },
7889
7890         /**
7891          * Set the width of the element
7892          * @param {Number} width The new width
7893          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7894          * @return {Roo.Element} this
7895          */
7896         setWidth : function(width, animate){
7897             width = this.adjustWidth(width);
7898             if(!animate || !A){
7899                 this.dom.style.width = this.addUnits(width);
7900             }else{
7901                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Set the height of the element
7908          * @param {Number} height The new height
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912          setHeight : function(height, animate){
7913             height = this.adjustHeight(height);
7914             if(!animate || !A){
7915                 this.dom.style.height = this.addUnits(height);
7916             }else{
7917                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7918             }
7919             return this;
7920         },
7921
7922         /**
7923          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7924          * @param {Number} width The new width
7925          * @param {Number} height The new height
7926          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7927          * @return {Roo.Element} this
7928          */
7929          setSize : function(width, height, animate){
7930             if(typeof width == "object"){ // in case of object from getSize()
7931                 height = width.height; width = width.width;
7932             }
7933             width = this.adjustWidth(width); height = this.adjustHeight(height);
7934             if(!animate || !A){
7935                 this.dom.style.width = this.addUnits(width);
7936                 this.dom.style.height = this.addUnits(height);
7937             }else{
7938                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7945          * @param {Number} x X value for new position (coordinates are page-based)
7946          * @param {Number} y Y value for new position (coordinates are page-based)
7947          * @param {Number} width The new width
7948          * @param {Number} height The new height
7949          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7950          * @return {Roo.Element} this
7951          */
7952         setBounds : function(x, y, width, height, animate){
7953             if(!animate || !A){
7954                 this.setSize(width, height);
7955                 this.setLocation(x, y);
7956             }else{
7957                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7958                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7959                               this.preanim(arguments, 4), 'motion');
7960             }
7961             return this;
7962         },
7963
7964         /**
7965          * 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.
7966          * @param {Roo.lib.Region} region The region to fill
7967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7968          * @return {Roo.Element} this
7969          */
7970         setRegion : function(region, animate){
7971             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7972             return this;
7973         },
7974
7975         /**
7976          * Appends an event handler
7977          *
7978          * @param {String}   eventName     The type of event to append
7979          * @param {Function} fn        The method the event invokes
7980          * @param {Object} scope       (optional) The scope (this object) of the fn
7981          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7982          */
7983         addListener : function(eventName, fn, scope, options){
7984             if (this.dom) {
7985                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7986             }
7987         },
7988
7989         /**
7990          * Removes an event handler from this element
7991          * @param {String} eventName the type of event to remove
7992          * @param {Function} fn the method the event invokes
7993          * @return {Roo.Element} this
7994          */
7995         removeListener : function(eventName, fn){
7996             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7997             return this;
7998         },
7999
8000         /**
8001          * Removes all previous added listeners from this element
8002          * @return {Roo.Element} this
8003          */
8004         removeAllListeners : function(){
8005             E.purgeElement(this.dom);
8006             return this;
8007         },
8008
8009         relayEvent : function(eventName, observable){
8010             this.on(eventName, function(e){
8011                 observable.fireEvent(eventName, e);
8012             });
8013         },
8014
8015         /**
8016          * Set the opacity of the element
8017          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8018          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8019          * @return {Roo.Element} this
8020          */
8021          setOpacity : function(opacity, animate){
8022             if(!animate || !A){
8023                 var s = this.dom.style;
8024                 if(Roo.isIE){
8025                     s.zoom = 1;
8026                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8027                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8028                 }else{
8029                     s.opacity = opacity;
8030                 }
8031             }else{
8032                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8033             }
8034             return this;
8035         },
8036
8037         /**
8038          * Gets the left X coordinate
8039          * @param {Boolean} local True to get the local css position instead of page coordinate
8040          * @return {Number}
8041          */
8042         getLeft : function(local){
8043             if(!local){
8044                 return this.getX();
8045             }else{
8046                 return parseInt(this.getStyle("left"), 10) || 0;
8047             }
8048         },
8049
8050         /**
8051          * Gets the right X coordinate of the element (element X position + element width)
8052          * @param {Boolean} local True to get the local css position instead of page coordinate
8053          * @return {Number}
8054          */
8055         getRight : function(local){
8056             if(!local){
8057                 return this.getX() + this.getWidth();
8058             }else{
8059                 return (this.getLeft(true) + this.getWidth()) || 0;
8060             }
8061         },
8062
8063         /**
8064          * Gets the top Y coordinate
8065          * @param {Boolean} local True to get the local css position instead of page coordinate
8066          * @return {Number}
8067          */
8068         getTop : function(local) {
8069             if(!local){
8070                 return this.getY();
8071             }else{
8072                 return parseInt(this.getStyle("top"), 10) || 0;
8073             }
8074         },
8075
8076         /**
8077          * Gets the bottom Y coordinate of the element (element Y position + element height)
8078          * @param {Boolean} local True to get the local css position instead of page coordinate
8079          * @return {Number}
8080          */
8081         getBottom : function(local){
8082             if(!local){
8083                 return this.getY() + this.getHeight();
8084             }else{
8085                 return (this.getTop(true) + this.getHeight()) || 0;
8086             }
8087         },
8088
8089         /**
8090         * Initializes positioning on this element. If a desired position is not passed, it will make the
8091         * the element positioned relative IF it is not already positioned.
8092         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8093         * @param {Number} zIndex (optional) The zIndex to apply
8094         * @param {Number} x (optional) Set the page X position
8095         * @param {Number} y (optional) Set the page Y position
8096         */
8097         position : function(pos, zIndex, x, y){
8098             if(!pos){
8099                if(this.getStyle('position') == 'static'){
8100                    this.setStyle('position', 'relative');
8101                }
8102             }else{
8103                 this.setStyle("position", pos);
8104             }
8105             if(zIndex){
8106                 this.setStyle("z-index", zIndex);
8107             }
8108             if(x !== undefined && y !== undefined){
8109                 this.setXY([x, y]);
8110             }else if(x !== undefined){
8111                 this.setX(x);
8112             }else if(y !== undefined){
8113                 this.setY(y);
8114             }
8115         },
8116
8117         /**
8118         * Clear positioning back to the default when the document was loaded
8119         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8120         * @return {Roo.Element} this
8121          */
8122         clearPositioning : function(value){
8123             value = value ||'';
8124             this.setStyle({
8125                 "left": value,
8126                 "right": value,
8127                 "top": value,
8128                 "bottom": value,
8129                 "z-index": "",
8130                 "position" : "static"
8131             });
8132             return this;
8133         },
8134
8135         /**
8136         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8137         * snapshot before performing an update and then restoring the element.
8138         * @return {Object}
8139         */
8140         getPositioning : function(){
8141             var l = this.getStyle("left");
8142             var t = this.getStyle("top");
8143             return {
8144                 "position" : this.getStyle("position"),
8145                 "left" : l,
8146                 "right" : l ? "" : this.getStyle("right"),
8147                 "top" : t,
8148                 "bottom" : t ? "" : this.getStyle("bottom"),
8149                 "z-index" : this.getStyle("z-index")
8150             };
8151         },
8152
8153         /**
8154          * Gets the width of the border(s) for the specified side(s)
8155          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8156          * passing lr would get the border (l)eft width + the border (r)ight width.
8157          * @return {Number} The width of the sides passed added together
8158          */
8159         getBorderWidth : function(side){
8160             return this.addStyles(side, El.borders);
8161         },
8162
8163         /**
8164          * Gets the width of the padding(s) for the specified side(s)
8165          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8166          * passing lr would get the padding (l)eft + the padding (r)ight.
8167          * @return {Number} The padding of the sides passed added together
8168          */
8169         getPadding : function(side){
8170             return this.addStyles(side, El.paddings);
8171         },
8172
8173         /**
8174         * Set positioning with an object returned by getPositioning().
8175         * @param {Object} posCfg
8176         * @return {Roo.Element} this
8177          */
8178         setPositioning : function(pc){
8179             this.applyStyles(pc);
8180             if(pc.right == "auto"){
8181                 this.dom.style.right = "";
8182             }
8183             if(pc.bottom == "auto"){
8184                 this.dom.style.bottom = "";
8185             }
8186             return this;
8187         },
8188
8189         // private
8190         fixDisplay : function(){
8191             if(this.getStyle("display") == "none"){
8192                 this.setStyle("visibility", "hidden");
8193                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8194                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8195                     this.setStyle("display", "block");
8196                 }
8197             }
8198         },
8199
8200         /**
8201          * Quick set left and top adding default units
8202          * @param {String} left The left CSS property value
8203          * @param {String} top The top CSS property value
8204          * @return {Roo.Element} this
8205          */
8206          setLeftTop : function(left, top){
8207             this.dom.style.left = this.addUnits(left);
8208             this.dom.style.top = this.addUnits(top);
8209             return this;
8210         },
8211
8212         /**
8213          * Move this element relative to its current position.
8214          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8215          * @param {Number} distance How far to move the element in pixels
8216          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8217          * @return {Roo.Element} this
8218          */
8219          move : function(direction, distance, animate){
8220             var xy = this.getXY();
8221             direction = direction.toLowerCase();
8222             switch(direction){
8223                 case "l":
8224                 case "left":
8225                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8226                     break;
8227                case "r":
8228                case "right":
8229                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8230                     break;
8231                case "t":
8232                case "top":
8233                case "up":
8234                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8235                     break;
8236                case "b":
8237                case "bottom":
8238                case "down":
8239                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8240                     break;
8241             }
8242             return this;
8243         },
8244
8245         /**
8246          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8247          * @return {Roo.Element} this
8248          */
8249         clip : function(){
8250             if(!this.isClipped){
8251                this.isClipped = true;
8252                this.originalClip = {
8253                    "o": this.getStyle("overflow"),
8254                    "x": this.getStyle("overflow-x"),
8255                    "y": this.getStyle("overflow-y")
8256                };
8257                this.setStyle("overflow", "hidden");
8258                this.setStyle("overflow-x", "hidden");
8259                this.setStyle("overflow-y", "hidden");
8260             }
8261             return this;
8262         },
8263
8264         /**
8265          *  Return clipping (overflow) to original clipping before clip() was called
8266          * @return {Roo.Element} this
8267          */
8268         unclip : function(){
8269             if(this.isClipped){
8270                 this.isClipped = false;
8271                 var o = this.originalClip;
8272                 if(o.o){this.setStyle("overflow", o.o);}
8273                 if(o.x){this.setStyle("overflow-x", o.x);}
8274                 if(o.y){this.setStyle("overflow-y", o.y);}
8275             }
8276             return this;
8277         },
8278
8279
8280         /**
8281          * Gets the x,y coordinates specified by the anchor position on the element.
8282          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8283          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8284          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8285          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8286          * @return {Array} [x, y] An array containing the element's x and y coordinates
8287          */
8288         getAnchorXY : function(anchor, local, s){
8289             //Passing a different size is useful for pre-calculating anchors,
8290             //especially for anchored animations that change the el size.
8291
8292             var w, h, vp = false;
8293             if(!s){
8294                 var d = this.dom;
8295                 if(d == document.body || d == document){
8296                     vp = true;
8297                     w = D.getViewWidth(); h = D.getViewHeight();
8298                 }else{
8299                     w = this.getWidth(); h = this.getHeight();
8300                 }
8301             }else{
8302                 w = s.width;  h = s.height;
8303             }
8304             var x = 0, y = 0, r = Math.round;
8305             switch((anchor || "tl").toLowerCase()){
8306                 case "c":
8307                     x = r(w*.5);
8308                     y = r(h*.5);
8309                 break;
8310                 case "t":
8311                     x = r(w*.5);
8312                     y = 0;
8313                 break;
8314                 case "l":
8315                     x = 0;
8316                     y = r(h*.5);
8317                 break;
8318                 case "r":
8319                     x = w;
8320                     y = r(h*.5);
8321                 break;
8322                 case "b":
8323                     x = r(w*.5);
8324                     y = h;
8325                 break;
8326                 case "tl":
8327                     x = 0;
8328                     y = 0;
8329                 break;
8330                 case "bl":
8331                     x = 0;
8332                     y = h;
8333                 break;
8334                 case "br":
8335                     x = w;
8336                     y = h;
8337                 break;
8338                 case "tr":
8339                     x = w;
8340                     y = 0;
8341                 break;
8342             }
8343             if(local === true){
8344                 return [x, y];
8345             }
8346             if(vp){
8347                 var sc = this.getScroll();
8348                 return [x + sc.left, y + sc.top];
8349             }
8350             //Add the element's offset xy
8351             var o = this.getXY();
8352             return [x+o[0], y+o[1]];
8353         },
8354
8355         /**
8356          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8357          * supported position values.
8358          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8359          * @param {String} position The position to align to.
8360          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8361          * @return {Array} [x, y]
8362          */
8363         getAlignToXY : function(el, p, o){
8364             el = Roo.get(el);
8365             var d = this.dom;
8366             if(!el.dom){
8367                 throw "Element.alignTo with an element that doesn't exist";
8368             }
8369             var c = false; //constrain to viewport
8370             var p1 = "", p2 = "";
8371             o = o || [0,0];
8372
8373             if(!p){
8374                 p = "tl-bl";
8375             }else if(p == "?"){
8376                 p = "tl-bl?";
8377             }else if(p.indexOf("-") == -1){
8378                 p = "tl-" + p;
8379             }
8380             p = p.toLowerCase();
8381             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8382             if(!m){
8383                throw "Element.alignTo with an invalid alignment " + p;
8384             }
8385             p1 = m[1]; p2 = m[2]; c = !!m[3];
8386
8387             //Subtract the aligned el's internal xy from the target's offset xy
8388             //plus custom offset to get the aligned el's new offset xy
8389             var a1 = this.getAnchorXY(p1, true);
8390             var a2 = el.getAnchorXY(p2, false);
8391             var x = a2[0] - a1[0] + o[0];
8392             var y = a2[1] - a1[1] + o[1];
8393             if(c){
8394                 //constrain the aligned el to viewport if necessary
8395                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8396                 // 5px of margin for ie
8397                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8398
8399                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8400                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8401                 //otherwise swap the aligned el to the opposite border of the target.
8402                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8403                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8404                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8405                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8406
8407                var doc = document;
8408                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8409                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8410
8411                if((x+w) > dw + scrollX){
8412                     x = swapX ? r.left-w : dw+scrollX-w;
8413                 }
8414                if(x < scrollX){
8415                    x = swapX ? r.right : scrollX;
8416                }
8417                if((y+h) > dh + scrollY){
8418                     y = swapY ? r.top-h : dh+scrollY-h;
8419                 }
8420                if (y < scrollY){
8421                    y = swapY ? r.bottom : scrollY;
8422                }
8423             }
8424             return [x,y];
8425         },
8426
8427         // private
8428         getConstrainToXY : function(){
8429             var os = {top:0, left:0, bottom:0, right: 0};
8430
8431             return function(el, local, offsets, proposedXY){
8432                 el = Roo.get(el);
8433                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8434
8435                 var vw, vh, vx = 0, vy = 0;
8436                 if(el.dom == document.body || el.dom == document){
8437                     vw = Roo.lib.Dom.getViewWidth();
8438                     vh = Roo.lib.Dom.getViewHeight();
8439                 }else{
8440                     vw = el.dom.clientWidth;
8441                     vh = el.dom.clientHeight;
8442                     if(!local){
8443                         var vxy = el.getXY();
8444                         vx = vxy[0];
8445                         vy = vxy[1];
8446                     }
8447                 }
8448
8449                 var s = el.getScroll();
8450
8451                 vx += offsets.left + s.left;
8452                 vy += offsets.top + s.top;
8453
8454                 vw -= offsets.right;
8455                 vh -= offsets.bottom;
8456
8457                 var vr = vx+vw;
8458                 var vb = vy+vh;
8459
8460                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8461                 var x = xy[0], y = xy[1];
8462                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8463
8464                 // only move it if it needs it
8465                 var moved = false;
8466
8467                 // first validate right/bottom
8468                 if((x + w) > vr){
8469                     x = vr - w;
8470                     moved = true;
8471                 }
8472                 if((y + h) > vb){
8473                     y = vb - h;
8474                     moved = true;
8475                 }
8476                 // then make sure top/left isn't negative
8477                 if(x < vx){
8478                     x = vx;
8479                     moved = true;
8480                 }
8481                 if(y < vy){
8482                     y = vy;
8483                     moved = true;
8484                 }
8485                 return moved ? [x, y] : false;
8486             };
8487         }(),
8488
8489         // private
8490         adjustForConstraints : function(xy, parent, offsets){
8491             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8492         },
8493
8494         /**
8495          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8496          * document it aligns it to the viewport.
8497          * The position parameter is optional, and can be specified in any one of the following formats:
8498          * <ul>
8499          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8500          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8501          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8502          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8503          *   <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
8504          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8505          * </ul>
8506          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8507          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8508          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8509          * that specified in order to enforce the viewport constraints.
8510          * Following are all of the supported anchor positions:
8511     <pre>
8512     Value  Description
8513     -----  -----------------------------
8514     tl     The top left corner (default)
8515     t      The center of the top edge
8516     tr     The top right corner
8517     l      The center of the left edge
8518     c      In the center of the element
8519     r      The center of the right edge
8520     bl     The bottom left corner
8521     b      The center of the bottom edge
8522     br     The bottom right corner
8523     </pre>
8524     Example Usage:
8525     <pre><code>
8526     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8527     el.alignTo("other-el");
8528
8529     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8530     el.alignTo("other-el", "tr?");
8531
8532     // align the bottom right corner of el with the center left edge of other-el
8533     el.alignTo("other-el", "br-l?");
8534
8535     // align the center of el with the bottom left corner of other-el and
8536     // adjust the x position by -6 pixels (and the y position by 0)
8537     el.alignTo("other-el", "c-bl", [-6, 0]);
8538     </code></pre>
8539          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8540          * @param {String} position The position to align to.
8541          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8542          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8543          * @return {Roo.Element} this
8544          */
8545         alignTo : function(element, position, offsets, animate){
8546             var xy = this.getAlignToXY(element, position, offsets);
8547             this.setXY(xy, this.preanim(arguments, 3));
8548             return this;
8549         },
8550
8551         /**
8552          * Anchors an element to another element and realigns it when the window is resized.
8553          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8554          * @param {String} position The position to align to.
8555          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8556          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8557          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8558          * is a number, it is used as the buffer delay (defaults to 50ms).
8559          * @param {Function} callback The function to call after the animation finishes
8560          * @return {Roo.Element} this
8561          */
8562         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8563             var action = function(){
8564                 this.alignTo(el, alignment, offsets, animate);
8565                 Roo.callback(callback, this);
8566             };
8567             Roo.EventManager.onWindowResize(action, this);
8568             var tm = typeof monitorScroll;
8569             if(tm != 'undefined'){
8570                 Roo.EventManager.on(window, 'scroll', action, this,
8571                     {buffer: tm == 'number' ? monitorScroll : 50});
8572             }
8573             action.call(this); // align immediately
8574             return this;
8575         },
8576         /**
8577          * Clears any opacity settings from this element. Required in some cases for IE.
8578          * @return {Roo.Element} this
8579          */
8580         clearOpacity : function(){
8581             if (window.ActiveXObject) {
8582                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8583                     this.dom.style.filter = "";
8584                 }
8585             } else {
8586                 this.dom.style.opacity = "";
8587                 this.dom.style["-moz-opacity"] = "";
8588                 this.dom.style["-khtml-opacity"] = "";
8589             }
8590             return this;
8591         },
8592
8593         /**
8594          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8595          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8596          * @return {Roo.Element} this
8597          */
8598         hide : function(animate){
8599             this.setVisible(false, this.preanim(arguments, 0));
8600             return this;
8601         },
8602
8603         /**
8604         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8605         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8606          * @return {Roo.Element} this
8607          */
8608         show : function(animate){
8609             this.setVisible(true, this.preanim(arguments, 0));
8610             return this;
8611         },
8612
8613         /**
8614          * @private Test if size has a unit, otherwise appends the default
8615          */
8616         addUnits : function(size){
8617             return Roo.Element.addUnits(size, this.defaultUnit);
8618         },
8619
8620         /**
8621          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8622          * @return {Roo.Element} this
8623          */
8624         beginMeasure : function(){
8625             var el = this.dom;
8626             if(el.offsetWidth || el.offsetHeight){
8627                 return this; // offsets work already
8628             }
8629             var changed = [];
8630             var p = this.dom, b = document.body; // start with this element
8631             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8632                 var pe = Roo.get(p);
8633                 if(pe.getStyle('display') == 'none'){
8634                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8635                     p.style.visibility = "hidden";
8636                     p.style.display = "block";
8637                 }
8638                 p = p.parentNode;
8639             }
8640             this._measureChanged = changed;
8641             return this;
8642
8643         },
8644
8645         /**
8646          * Restores displays to before beginMeasure was called
8647          * @return {Roo.Element} this
8648          */
8649         endMeasure : function(){
8650             var changed = this._measureChanged;
8651             if(changed){
8652                 for(var i = 0, len = changed.length; i < len; i++) {
8653                     var r = changed[i];
8654                     r.el.style.visibility = r.visibility;
8655                     r.el.style.display = "none";
8656                 }
8657                 this._measureChanged = null;
8658             }
8659             return this;
8660         },
8661
8662         /**
8663         * Update the innerHTML of this element, optionally searching for and processing scripts
8664         * @param {String} html The new HTML
8665         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8666         * @param {Function} callback For async script loading you can be noticed when the update completes
8667         * @return {Roo.Element} this
8668          */
8669         update : function(html, loadScripts, callback){
8670             if(typeof html == "undefined"){
8671                 html = "";
8672             }
8673             if(loadScripts !== true){
8674                 this.dom.innerHTML = html;
8675                 if(typeof callback == "function"){
8676                     callback();
8677                 }
8678                 return this;
8679             }
8680             var id = Roo.id();
8681             var dom = this.dom;
8682
8683             html += '<span id="' + id + '"></span>';
8684
8685             E.onAvailable(id, function(){
8686                 var hd = document.getElementsByTagName("head")[0];
8687                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8688                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8689                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8690
8691                 var match;
8692                 while(match = re.exec(html)){
8693                     var attrs = match[1];
8694                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8695                     if(srcMatch && srcMatch[2]){
8696                        var s = document.createElement("script");
8697                        s.src = srcMatch[2];
8698                        var typeMatch = attrs.match(typeRe);
8699                        if(typeMatch && typeMatch[2]){
8700                            s.type = typeMatch[2];
8701                        }
8702                        hd.appendChild(s);
8703                     }else if(match[2] && match[2].length > 0){
8704                         if(window.execScript) {
8705                            window.execScript(match[2]);
8706                         } else {
8707                             /**
8708                              * eval:var:id
8709                              * eval:var:dom
8710                              * eval:var:html
8711                              * 
8712                              */
8713                            window.eval(match[2]);
8714                         }
8715                     }
8716                 }
8717                 var el = document.getElementById(id);
8718                 if(el){el.parentNode.removeChild(el);}
8719                 if(typeof callback == "function"){
8720                     callback();
8721                 }
8722             });
8723             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8724             return this;
8725         },
8726
8727         /**
8728          * Direct access to the UpdateManager update() method (takes the same parameters).
8729          * @param {String/Function} url The url for this request or a function to call to get the url
8730          * @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}
8731          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8732          * @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.
8733          * @return {Roo.Element} this
8734          */
8735         load : function(){
8736             var um = this.getUpdateManager();
8737             um.update.apply(um, arguments);
8738             return this;
8739         },
8740
8741         /**
8742         * Gets this element's UpdateManager
8743         * @return {Roo.UpdateManager} The UpdateManager
8744         */
8745         getUpdateManager : function(){
8746             if(!this.updateManager){
8747                 this.updateManager = new Roo.UpdateManager(this);
8748             }
8749             return this.updateManager;
8750         },
8751
8752         /**
8753          * Disables text selection for this element (normalized across browsers)
8754          * @return {Roo.Element} this
8755          */
8756         unselectable : function(){
8757             this.dom.unselectable = "on";
8758             this.swallowEvent("selectstart", true);
8759             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8760             this.addClass("x-unselectable");
8761             return this;
8762         },
8763
8764         /**
8765         * Calculates the x, y to center this element on the screen
8766         * @return {Array} The x, y values [x, y]
8767         */
8768         getCenterXY : function(){
8769             return this.getAlignToXY(document, 'c-c');
8770         },
8771
8772         /**
8773         * Centers the Element in either the viewport, or another Element.
8774         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8775         */
8776         center : function(centerIn){
8777             this.alignTo(centerIn || document, 'c-c');
8778             return this;
8779         },
8780
8781         /**
8782          * Tests various css rules/browsers to determine if this element uses a border box
8783          * @return {Boolean}
8784          */
8785         isBorderBox : function(){
8786             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8787         },
8788
8789         /**
8790          * Return a box {x, y, width, height} that can be used to set another elements
8791          * size/location to match this element.
8792          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8793          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8794          * @return {Object} box An object in the format {x, y, width, height}
8795          */
8796         getBox : function(contentBox, local){
8797             var xy;
8798             if(!local){
8799                 xy = this.getXY();
8800             }else{
8801                 var left = parseInt(this.getStyle("left"), 10) || 0;
8802                 var top = parseInt(this.getStyle("top"), 10) || 0;
8803                 xy = [left, top];
8804             }
8805             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8806             if(!contentBox){
8807                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8808             }else{
8809                 var l = this.getBorderWidth("l")+this.getPadding("l");
8810                 var r = this.getBorderWidth("r")+this.getPadding("r");
8811                 var t = this.getBorderWidth("t")+this.getPadding("t");
8812                 var b = this.getBorderWidth("b")+this.getPadding("b");
8813                 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)};
8814             }
8815             bx.right = bx.x + bx.width;
8816             bx.bottom = bx.y + bx.height;
8817             return bx;
8818         },
8819
8820         /**
8821          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8822          for more information about the sides.
8823          * @param {String} sides
8824          * @return {Number}
8825          */
8826         getFrameWidth : function(sides, onlyContentBox){
8827             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8828         },
8829
8830         /**
8831          * 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.
8832          * @param {Object} box The box to fill {x, y, width, height}
8833          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8834          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8835          * @return {Roo.Element} this
8836          */
8837         setBox : function(box, adjust, animate){
8838             var w = box.width, h = box.height;
8839             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8840                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8841                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8842             }
8843             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8844             return this;
8845         },
8846
8847         /**
8848          * Forces the browser to repaint this element
8849          * @return {Roo.Element} this
8850          */
8851          repaint : function(){
8852             var dom = this.dom;
8853             this.addClass("x-repaint");
8854             setTimeout(function(){
8855                 Roo.get(dom).removeClass("x-repaint");
8856             }, 1);
8857             return this;
8858         },
8859
8860         /**
8861          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8862          * then it returns the calculated width of the sides (see getPadding)
8863          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8864          * @return {Object/Number}
8865          */
8866         getMargins : function(side){
8867             if(!side){
8868                 return {
8869                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8870                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8871                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8872                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8873                 };
8874             }else{
8875                 return this.addStyles(side, El.margins);
8876              }
8877         },
8878
8879         // private
8880         addStyles : function(sides, styles){
8881             var val = 0, v, w;
8882             for(var i = 0, len = sides.length; i < len; i++){
8883                 v = this.getStyle(styles[sides.charAt(i)]);
8884                 if(v){
8885                      w = parseInt(v, 10);
8886                      if(w){ val += w; }
8887                 }
8888             }
8889             return val;
8890         },
8891
8892         /**
8893          * Creates a proxy element of this element
8894          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8895          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8896          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8897          * @return {Roo.Element} The new proxy element
8898          */
8899         createProxy : function(config, renderTo, matchBox){
8900             if(renderTo){
8901                 renderTo = Roo.getDom(renderTo);
8902             }else{
8903                 renderTo = document.body;
8904             }
8905             config = typeof config == "object" ?
8906                 config : {tag : "div", cls: config};
8907             var proxy = Roo.DomHelper.append(renderTo, config, true);
8908             if(matchBox){
8909                proxy.setBox(this.getBox());
8910             }
8911             return proxy;
8912         },
8913
8914         /**
8915          * Puts a mask over this element to disable user interaction. Requires core.css.
8916          * This method can only be applied to elements which accept child nodes.
8917          * @param {String} msg (optional) A message to display in the mask
8918          * @param {String} msgCls (optional) A css class to apply to the msg element
8919          * @return {Element} The mask  element
8920          */
8921         mask : function(msg, msgCls)
8922         {
8923             if(this.getStyle("position") == "static"){
8924                 this.setStyle("position", "relative");
8925             }
8926             if(!this._mask){
8927                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8928             }
8929             this.addClass("x-masked");
8930             this._mask.setDisplayed(true);
8931             
8932             // we wander
8933             var z = 0;
8934             var dom = this.dom
8935             while (dom && dom.style) {
8936                 if (!isNaN(parseInt(dom.style.zIndex))) {
8937                     z = Math.max(z, parseInt(dom.style.zIndex));
8938                 }
8939                 dom = dom.parentNode;
8940             }
8941             // if we are masking the body - then it hides everything..
8942             if (this.dom == document.body) {
8943                 z = 1000000;
8944                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8945                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8946             }
8947            
8948             if(typeof msg == 'string'){
8949                 if(!this._maskMsg){
8950                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8951                 }
8952                 var mm = this._maskMsg;
8953                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8954                 mm.dom.firstChild.innerHTML = msg;
8955                 mm.setDisplayed(true);
8956                 mm.center(this);
8957                 mm.setStyle('z-index', z + 102);
8958             }
8959             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8960                 this._mask.setHeight(this.getHeight());
8961             }
8962             this._mask.setStyle('z-index', z + 100);
8963             
8964             return this._mask;
8965         },
8966
8967         /**
8968          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8969          * it is cached for reuse.
8970          */
8971         unmask : function(removeEl){
8972             if(this._mask){
8973                 if(removeEl === true){
8974                     this._mask.remove();
8975                     delete this._mask;
8976                     if(this._maskMsg){
8977                         this._maskMsg.remove();
8978                         delete this._maskMsg;
8979                     }
8980                 }else{
8981                     this._mask.setDisplayed(false);
8982                     if(this._maskMsg){
8983                         this._maskMsg.setDisplayed(false);
8984                     }
8985                 }
8986             }
8987             this.removeClass("x-masked");
8988         },
8989
8990         /**
8991          * Returns true if this element is masked
8992          * @return {Boolean}
8993          */
8994         isMasked : function(){
8995             return this._mask && this._mask.isVisible();
8996         },
8997
8998         /**
8999          * Creates an iframe shim for this element to keep selects and other windowed objects from
9000          * showing through.
9001          * @return {Roo.Element} The new shim element
9002          */
9003         createShim : function(){
9004             var el = document.createElement('iframe');
9005             el.frameBorder = 'no';
9006             el.className = 'roo-shim';
9007             if(Roo.isIE && Roo.isSecure){
9008                 el.src = Roo.SSL_SECURE_URL;
9009             }
9010             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9011             shim.autoBoxAdjust = false;
9012             return shim;
9013         },
9014
9015         /**
9016          * Removes this element from the DOM and deletes it from the cache
9017          */
9018         remove : function(){
9019             if(this.dom.parentNode){
9020                 this.dom.parentNode.removeChild(this.dom);
9021             }
9022             delete El.cache[this.dom.id];
9023         },
9024
9025         /**
9026          * Sets up event handlers to add and remove a css class when the mouse is over this element
9027          * @param {String} className
9028          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9029          * mouseout events for children elements
9030          * @return {Roo.Element} this
9031          */
9032         addClassOnOver : function(className, preventFlicker){
9033             this.on("mouseover", function(){
9034                 Roo.fly(this, '_internal').addClass(className);
9035             }, this.dom);
9036             var removeFn = function(e){
9037                 if(preventFlicker !== true || !e.within(this, true)){
9038                     Roo.fly(this, '_internal').removeClass(className);
9039                 }
9040             };
9041             this.on("mouseout", removeFn, this.dom);
9042             return this;
9043         },
9044
9045         /**
9046          * Sets up event handlers to add and remove a css class when this element has the focus
9047          * @param {String} className
9048          * @return {Roo.Element} this
9049          */
9050         addClassOnFocus : function(className){
9051             this.on("focus", function(){
9052                 Roo.fly(this, '_internal').addClass(className);
9053             }, this.dom);
9054             this.on("blur", function(){
9055                 Roo.fly(this, '_internal').removeClass(className);
9056             }, this.dom);
9057             return this;
9058         },
9059         /**
9060          * 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)
9061          * @param {String} className
9062          * @return {Roo.Element} this
9063          */
9064         addClassOnClick : function(className){
9065             var dom = this.dom;
9066             this.on("mousedown", function(){
9067                 Roo.fly(dom, '_internal').addClass(className);
9068                 var d = Roo.get(document);
9069                 var fn = function(){
9070                     Roo.fly(dom, '_internal').removeClass(className);
9071                     d.removeListener("mouseup", fn);
9072                 };
9073                 d.on("mouseup", fn);
9074             });
9075             return this;
9076         },
9077
9078         /**
9079          * Stops the specified event from bubbling and optionally prevents the default action
9080          * @param {String} eventName
9081          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9082          * @return {Roo.Element} this
9083          */
9084         swallowEvent : function(eventName, preventDefault){
9085             var fn = function(e){
9086                 e.stopPropagation();
9087                 if(preventDefault){
9088                     e.preventDefault();
9089                 }
9090             };
9091             if(eventName instanceof Array){
9092                 for(var i = 0, len = eventName.length; i < len; i++){
9093                      this.on(eventName[i], fn);
9094                 }
9095                 return this;
9096             }
9097             this.on(eventName, fn);
9098             return this;
9099         },
9100
9101         /**
9102          * @private
9103          */
9104       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9105
9106         /**
9107          * Sizes this element to its parent element's dimensions performing
9108          * neccessary box adjustments.
9109          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9110          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9111          * @return {Roo.Element} this
9112          */
9113         fitToParent : function(monitorResize, targetParent) {
9114           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9115           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9116           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9117             return;
9118           }
9119           var p = Roo.get(targetParent || this.dom.parentNode);
9120           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9121           if (monitorResize === true) {
9122             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9123             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9124           }
9125           return this;
9126         },
9127
9128         /**
9129          * Gets the next sibling, skipping text nodes
9130          * @return {HTMLElement} The next sibling or null
9131          */
9132         getNextSibling : function(){
9133             var n = this.dom.nextSibling;
9134             while(n && n.nodeType != 1){
9135                 n = n.nextSibling;
9136             }
9137             return n;
9138         },
9139
9140         /**
9141          * Gets the previous sibling, skipping text nodes
9142          * @return {HTMLElement} The previous sibling or null
9143          */
9144         getPrevSibling : function(){
9145             var n = this.dom.previousSibling;
9146             while(n && n.nodeType != 1){
9147                 n = n.previousSibling;
9148             }
9149             return n;
9150         },
9151
9152
9153         /**
9154          * Appends the passed element(s) to this element
9155          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9156          * @return {Roo.Element} this
9157          */
9158         appendChild: function(el){
9159             el = Roo.get(el);
9160             el.appendTo(this);
9161             return this;
9162         },
9163
9164         /**
9165          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9166          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9167          * automatically generated with the specified attributes.
9168          * @param {HTMLElement} insertBefore (optional) a child element of this element
9169          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9170          * @return {Roo.Element} The new child element
9171          */
9172         createChild: function(config, insertBefore, returnDom){
9173             config = config || {tag:'div'};
9174             if(insertBefore){
9175                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9176             }
9177             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9178         },
9179
9180         /**
9181          * Appends this element to the passed element
9182          * @param {String/HTMLElement/Element} el The new parent element
9183          * @return {Roo.Element} this
9184          */
9185         appendTo: function(el){
9186             el = Roo.getDom(el);
9187             el.appendChild(this.dom);
9188             return this;
9189         },
9190
9191         /**
9192          * Inserts this element before the passed element in the DOM
9193          * @param {String/HTMLElement/Element} el The element to insert before
9194          * @return {Roo.Element} this
9195          */
9196         insertBefore: function(el){
9197             el = Roo.getDom(el);
9198             el.parentNode.insertBefore(this.dom, el);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element after the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert after
9205          * @return {Roo.Element} this
9206          */
9207         insertAfter: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el.nextSibling);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9215          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9216          * @return {Roo.Element} The new child
9217          */
9218         insertFirst: function(el, returnDom){
9219             el = el || {};
9220             if(typeof el == 'object' && !el.nodeType){ // dh config
9221                 return this.createChild(el, this.dom.firstChild, returnDom);
9222             }else{
9223                 el = Roo.getDom(el);
9224                 this.dom.insertBefore(el, this.dom.firstChild);
9225                 return !returnDom ? Roo.get(el) : el;
9226             }
9227         },
9228
9229         /**
9230          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9231          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9232          * @param {String} where (optional) 'before' or 'after' defaults to before
9233          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9234          * @return {Roo.Element} the inserted Element
9235          */
9236         insertSibling: function(el, where, returnDom){
9237             where = where ? where.toLowerCase() : 'before';
9238             el = el || {};
9239             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9240
9241             if(typeof el == 'object' && !el.nodeType){ // dh config
9242                 if(where == 'after' && !this.dom.nextSibling){
9243                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9244                 }else{
9245                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9246                 }
9247
9248             }else{
9249                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9250                             where == 'before' ? this.dom : this.dom.nextSibling);
9251                 if(!returnDom){
9252                     rt = Roo.get(rt);
9253                 }
9254             }
9255             return rt;
9256         },
9257
9258         /**
9259          * Creates and wraps this element with another element
9260          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9261          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9262          * @return {HTMLElement/Element} The newly created wrapper element
9263          */
9264         wrap: function(config, returnDom){
9265             if(!config){
9266                 config = {tag: "div"};
9267             }
9268             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9269             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9270             return newEl;
9271         },
9272
9273         /**
9274          * Replaces the passed element with this element
9275          * @param {String/HTMLElement/Element} el The element to replace
9276          * @return {Roo.Element} this
9277          */
9278         replace: function(el){
9279             el = Roo.get(el);
9280             this.insertBefore(el);
9281             el.remove();
9282             return this;
9283         },
9284
9285         /**
9286          * Inserts an html fragment into this element
9287          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9288          * @param {String} html The HTML fragment
9289          * @param {Boolean} returnEl True to return an Roo.Element
9290          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9291          */
9292         insertHtml : function(where, html, returnEl){
9293             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9294             return returnEl ? Roo.get(el) : el;
9295         },
9296
9297         /**
9298          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9299          * @param {Object} o The object with the attributes
9300          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9301          * @return {Roo.Element} this
9302          */
9303         set : function(o, useSet){
9304             var el = this.dom;
9305             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9306             for(var attr in o){
9307                 if(attr == "style" || typeof o[attr] == "function") continue;
9308                 if(attr=="cls"){
9309                     el.className = o["cls"];
9310                 }else{
9311                     if(useSet) el.setAttribute(attr, o[attr]);
9312                     else el[attr] = o[attr];
9313                 }
9314             }
9315             if(o.style){
9316                 Roo.DomHelper.applyStyles(el, o.style);
9317             }
9318             return this;
9319         },
9320
9321         /**
9322          * Convenience method for constructing a KeyMap
9323          * @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:
9324          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9325          * @param {Function} fn The function to call
9326          * @param {Object} scope (optional) The scope of the function
9327          * @return {Roo.KeyMap} The KeyMap created
9328          */
9329         addKeyListener : function(key, fn, scope){
9330             var config;
9331             if(typeof key != "object" || key instanceof Array){
9332                 config = {
9333                     key: key,
9334                     fn: fn,
9335                     scope: scope
9336                 };
9337             }else{
9338                 config = {
9339                     key : key.key,
9340                     shift : key.shift,
9341                     ctrl : key.ctrl,
9342                     alt : key.alt,
9343                     fn: fn,
9344                     scope: scope
9345                 };
9346             }
9347             return new Roo.KeyMap(this, config);
9348         },
9349
9350         /**
9351          * Creates a KeyMap for this element
9352          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9353          * @return {Roo.KeyMap} The KeyMap created
9354          */
9355         addKeyMap : function(config){
9356             return new Roo.KeyMap(this, config);
9357         },
9358
9359         /**
9360          * Returns true if this element is scrollable.
9361          * @return {Boolean}
9362          */
9363          isScrollable : function(){
9364             var dom = this.dom;
9365             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9366         },
9367
9368         /**
9369          * 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().
9370          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9371          * @param {Number} value The new scroll value
9372          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9373          * @return {Element} this
9374          */
9375
9376         scrollTo : function(side, value, animate){
9377             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9378             if(!animate || !A){
9379                 this.dom[prop] = value;
9380             }else{
9381                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9382                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9383             }
9384             return this;
9385         },
9386
9387         /**
9388          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9389          * within this element's scrollable range.
9390          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9391          * @param {Number} distance How far to scroll the element in pixels
9392          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9393          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9394          * was scrolled as far as it could go.
9395          */
9396          scroll : function(direction, distance, animate){
9397              if(!this.isScrollable()){
9398                  return;
9399              }
9400              var el = this.dom;
9401              var l = el.scrollLeft, t = el.scrollTop;
9402              var w = el.scrollWidth, h = el.scrollHeight;
9403              var cw = el.clientWidth, ch = el.clientHeight;
9404              direction = direction.toLowerCase();
9405              var scrolled = false;
9406              var a = this.preanim(arguments, 2);
9407              switch(direction){
9408                  case "l":
9409                  case "left":
9410                      if(w - l > cw){
9411                          var v = Math.min(l + distance, w-cw);
9412                          this.scrollTo("left", v, a);
9413                          scrolled = true;
9414                      }
9415                      break;
9416                 case "r":
9417                 case "right":
9418                      if(l > 0){
9419                          var v = Math.max(l - distance, 0);
9420                          this.scrollTo("left", v, a);
9421                          scrolled = true;
9422                      }
9423                      break;
9424                 case "t":
9425                 case "top":
9426                 case "up":
9427                      if(t > 0){
9428                          var v = Math.max(t - distance, 0);
9429                          this.scrollTo("top", v, a);
9430                          scrolled = true;
9431                      }
9432                      break;
9433                 case "b":
9434                 case "bottom":
9435                 case "down":
9436                      if(h - t > ch){
9437                          var v = Math.min(t + distance, h-ch);
9438                          this.scrollTo("top", v, a);
9439                          scrolled = true;
9440                      }
9441                      break;
9442              }
9443              return scrolled;
9444         },
9445
9446         /**
9447          * Translates the passed page coordinates into left/top css values for this element
9448          * @param {Number/Array} x The page x or an array containing [x, y]
9449          * @param {Number} y The page y
9450          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9451          */
9452         translatePoints : function(x, y){
9453             if(typeof x == 'object' || x instanceof Array){
9454                 y = x[1]; x = x[0];
9455             }
9456             var p = this.getStyle('position');
9457             var o = this.getXY();
9458
9459             var l = parseInt(this.getStyle('left'), 10);
9460             var t = parseInt(this.getStyle('top'), 10);
9461
9462             if(isNaN(l)){
9463                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9464             }
9465             if(isNaN(t)){
9466                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9467             }
9468
9469             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9470         },
9471
9472         /**
9473          * Returns the current scroll position of the element.
9474          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9475          */
9476         getScroll : function(){
9477             var d = this.dom, doc = document;
9478             if(d == doc || d == doc.body){
9479                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9480                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9481                 return {left: l, top: t};
9482             }else{
9483                 return {left: d.scrollLeft, top: d.scrollTop};
9484             }
9485         },
9486
9487         /**
9488          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9489          * are convert to standard 6 digit hex color.
9490          * @param {String} attr The css attribute
9491          * @param {String} defaultValue The default value to use when a valid color isn't found
9492          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9493          * YUI color anims.
9494          */
9495         getColor : function(attr, defaultValue, prefix){
9496             var v = this.getStyle(attr);
9497             if(!v || v == "transparent" || v == "inherit") {
9498                 return defaultValue;
9499             }
9500             var color = typeof prefix == "undefined" ? "#" : prefix;
9501             if(v.substr(0, 4) == "rgb("){
9502                 var rvs = v.slice(4, v.length -1).split(",");
9503                 for(var i = 0; i < 3; i++){
9504                     var h = parseInt(rvs[i]).toString(16);
9505                     if(h < 16){
9506                         h = "0" + h;
9507                     }
9508                     color += h;
9509                 }
9510             } else {
9511                 if(v.substr(0, 1) == "#"){
9512                     if(v.length == 4) {
9513                         for(var i = 1; i < 4; i++){
9514                             var c = v.charAt(i);
9515                             color +=  c + c;
9516                         }
9517                     }else if(v.length == 7){
9518                         color += v.substr(1);
9519                     }
9520                 }
9521             }
9522             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9523         },
9524
9525         /**
9526          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9527          * gradient background, rounded corners and a 4-way shadow.
9528          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9529          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9530          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9531          * @return {Roo.Element} this
9532          */
9533         boxWrap : function(cls){
9534             cls = cls || 'x-box';
9535             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9536             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9537             return el;
9538         },
9539
9540         /**
9541          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9542          * @param {String} namespace The namespace in which to look for the attribute
9543          * @param {String} name The attribute name
9544          * @return {String} The attribute value
9545          */
9546         getAttributeNS : Roo.isIE ? function(ns, name){
9547             var d = this.dom;
9548             var type = typeof d[ns+":"+name];
9549             if(type != 'undefined' && type != 'unknown'){
9550                 return d[ns+":"+name];
9551             }
9552             return d[name];
9553         } : function(ns, name){
9554             var d = this.dom;
9555             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9556         }
9557     };
9558
9559     var ep = El.prototype;
9560
9561     /**
9562      * Appends an event handler (Shorthand for addListener)
9563      * @param {String}   eventName     The type of event to append
9564      * @param {Function} fn        The method the event invokes
9565      * @param {Object} scope       (optional) The scope (this object) of the fn
9566      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9567      * @method
9568      */
9569     ep.on = ep.addListener;
9570         // backwards compat
9571     ep.mon = ep.addListener;
9572
9573     /**
9574      * Removes an event handler from this element (shorthand for removeListener)
9575      * @param {String} eventName the type of event to remove
9576      * @param {Function} fn the method the event invokes
9577      * @return {Roo.Element} this
9578      * @method
9579      */
9580     ep.un = ep.removeListener;
9581
9582     /**
9583      * true to automatically adjust width and height settings for box-model issues (default to true)
9584      */
9585     ep.autoBoxAdjust = true;
9586
9587     // private
9588     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9589
9590     // private
9591     El.addUnits = function(v, defaultUnit){
9592         if(v === "" || v == "auto"){
9593             return v;
9594         }
9595         if(v === undefined){
9596             return '';
9597         }
9598         if(typeof v == "number" || !El.unitPattern.test(v)){
9599             return v + (defaultUnit || 'px');
9600         }
9601         return v;
9602     };
9603
9604     // special markup used throughout Roo when box wrapping elements
9605     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>';
9606     /**
9607      * Visibility mode constant - Use visibility to hide element
9608      * @static
9609      * @type Number
9610      */
9611     El.VISIBILITY = 1;
9612     /**
9613      * Visibility mode constant - Use display to hide element
9614      * @static
9615      * @type Number
9616      */
9617     El.DISPLAY = 2;
9618
9619     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9620     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9621     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9622
9623
9624
9625     /**
9626      * @private
9627      */
9628     El.cache = {};
9629
9630     var docEl;
9631
9632     /**
9633      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9634      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9635      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9636      * @return {Element} The Element object
9637      * @static
9638      */
9639     El.get = function(el){
9640         var ex, elm, id;
9641         if(!el){ return null; }
9642         if(typeof el == "string"){ // element id
9643             if(!(elm = document.getElementById(el))){
9644                 return null;
9645             }
9646             if(ex = El.cache[el]){
9647                 ex.dom = elm;
9648             }else{
9649                 ex = El.cache[el] = new El(elm);
9650             }
9651             return ex;
9652         }else if(el.tagName){ // dom element
9653             if(!(id = el.id)){
9654                 id = Roo.id(el);
9655             }
9656             if(ex = El.cache[id]){
9657                 ex.dom = el;
9658             }else{
9659                 ex = El.cache[id] = new El(el);
9660             }
9661             return ex;
9662         }else if(el instanceof El){
9663             if(el != docEl){
9664                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9665                                                               // catch case where it hasn't been appended
9666                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9667             }
9668             return el;
9669         }else if(el.isComposite){
9670             return el;
9671         }else if(el instanceof Array){
9672             return El.select(el);
9673         }else if(el == document){
9674             // create a bogus element object representing the document object
9675             if(!docEl){
9676                 var f = function(){};
9677                 f.prototype = El.prototype;
9678                 docEl = new f();
9679                 docEl.dom = document;
9680             }
9681             return docEl;
9682         }
9683         return null;
9684     };
9685
9686     // private
9687     El.uncache = function(el){
9688         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9689             if(a[i]){
9690                 delete El.cache[a[i].id || a[i]];
9691             }
9692         }
9693     };
9694
9695     // private
9696     // Garbage collection - uncache elements/purge listeners on orphaned elements
9697     // so we don't hold a reference and cause the browser to retain them
9698     El.garbageCollect = function(){
9699         if(!Roo.enableGarbageCollector){
9700             clearInterval(El.collectorThread);
9701             return;
9702         }
9703         for(var eid in El.cache){
9704             var el = El.cache[eid], d = el.dom;
9705             // -------------------------------------------------------
9706             // Determining what is garbage:
9707             // -------------------------------------------------------
9708             // !d
9709             // dom node is null, definitely garbage
9710             // -------------------------------------------------------
9711             // !d.parentNode
9712             // no parentNode == direct orphan, definitely garbage
9713             // -------------------------------------------------------
9714             // !d.offsetParent && !document.getElementById(eid)
9715             // display none elements have no offsetParent so we will
9716             // also try to look it up by it's id. However, check
9717             // offsetParent first so we don't do unneeded lookups.
9718             // This enables collection of elements that are not orphans
9719             // directly, but somewhere up the line they have an orphan
9720             // parent.
9721             // -------------------------------------------------------
9722             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9723                 delete El.cache[eid];
9724                 if(d && Roo.enableListenerCollection){
9725                     E.purgeElement(d);
9726                 }
9727             }
9728         }
9729     }
9730     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9731
9732
9733     // dom is optional
9734     El.Flyweight = function(dom){
9735         this.dom = dom;
9736     };
9737     El.Flyweight.prototype = El.prototype;
9738
9739     El._flyweights = {};
9740     /**
9741      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9742      * the dom node can be overwritten by other code.
9743      * @param {String/HTMLElement} el The dom node or id
9744      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9745      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9746      * @static
9747      * @return {Element} The shared Element object
9748      */
9749     El.fly = function(el, named){
9750         named = named || '_global';
9751         el = Roo.getDom(el);
9752         if(!el){
9753             return null;
9754         }
9755         if(!El._flyweights[named]){
9756             El._flyweights[named] = new El.Flyweight();
9757         }
9758         El._flyweights[named].dom = el;
9759         return El._flyweights[named];
9760     };
9761
9762     /**
9763      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9764      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9765      * Shorthand of {@link Roo.Element#get}
9766      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9767      * @return {Element} The Element object
9768      * @member Roo
9769      * @method get
9770      */
9771     Roo.get = El.get;
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * Shorthand of {@link Roo.Element#fly}
9776      * @param {String/HTMLElement} el The dom node or id
9777      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9778      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9779      * @static
9780      * @return {Element} The shared Element object
9781      * @member Roo
9782      * @method fly
9783      */
9784     Roo.fly = El.fly;
9785
9786     // speedy lookup for elements never to box adjust
9787     var noBoxAdjust = Roo.isStrict ? {
9788         select:1
9789     } : {
9790         input:1, select:1, textarea:1
9791     };
9792     if(Roo.isIE || Roo.isGecko){
9793         noBoxAdjust['button'] = 1;
9794     }
9795
9796
9797     Roo.EventManager.on(window, 'unload', function(){
9798         delete El.cache;
9799         delete El._flyweights;
9800     });
9801 })();
9802
9803
9804
9805
9806 if(Roo.DomQuery){
9807     Roo.Element.selectorFunction = Roo.DomQuery.select;
9808 }
9809
9810 Roo.Element.select = function(selector, unique, root){
9811     var els;
9812     if(typeof selector == "string"){
9813         els = Roo.Element.selectorFunction(selector, root);
9814     }else if(selector.length !== undefined){
9815         els = selector;
9816     }else{
9817         throw "Invalid selector";
9818     }
9819     if(unique === true){
9820         return new Roo.CompositeElement(els);
9821     }else{
9822         return new Roo.CompositeElementLite(els);
9823     }
9824 };
9825 /**
9826  * Selects elements based on the passed CSS selector to enable working on them as 1.
9827  * @param {String/Array} selector The CSS selector or an array of elements
9828  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9829  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9830  * @return {CompositeElementLite/CompositeElement}
9831  * @member Roo
9832  * @method select
9833  */
9834 Roo.select = Roo.Element.select;
9835
9836
9837
9838
9839
9840
9841
9842
9843
9844
9845
9846
9847
9848
9849 /*
9850  * Based on:
9851  * Ext JS Library 1.1.1
9852  * Copyright(c) 2006-2007, Ext JS, LLC.
9853  *
9854  * Originally Released Under LGPL - original licence link has changed is not relivant.
9855  *
9856  * Fork - LGPL
9857  * <script type="text/javascript">
9858  */
9859
9860
9861
9862 //Notifies Element that fx methods are available
9863 Roo.enableFx = true;
9864
9865 /**
9866  * @class Roo.Fx
9867  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9868  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9869  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9870  * Element effects to work.</p><br/>
9871  *
9872  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9873  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9874  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9875  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9876  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9877  * expected results and should be done with care.</p><br/>
9878  *
9879  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9880  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9881 <pre>
9882 Value  Description
9883 -----  -----------------------------
9884 tl     The top left corner
9885 t      The center of the top edge
9886 tr     The top right corner
9887 l      The center of the left edge
9888 r      The center of the right edge
9889 bl     The bottom left corner
9890 b      The center of the bottom edge
9891 br     The bottom right corner
9892 </pre>
9893  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9894  * below are common options that can be passed to any Fx method.</b>
9895  * @cfg {Function} callback A function called when the effect is finished
9896  * @cfg {Object} scope The scope of the effect function
9897  * @cfg {String} easing A valid Easing value for the effect
9898  * @cfg {String} afterCls A css class to apply after the effect
9899  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9900  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9901  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9902  * effects that end with the element being visually hidden, ignored otherwise)
9903  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9904  * a function which returns such a specification that will be applied to the Element after the effect finishes
9905  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9906  * @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
9907  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9908  */
9909 Roo.Fx = {
9910         /**
9911          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9912          * origin for the slide effect.  This function automatically handles wrapping the element with
9913          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9914          * Usage:
9915          *<pre><code>
9916 // default: slide the element in from the top
9917 el.slideIn();
9918
9919 // custom: slide the element in from the right with a 2-second duration
9920 el.slideIn('r', { duration: 2 });
9921
9922 // common config options shown with default values
9923 el.slideIn('t', {
9924     easing: 'easeOut',
9925     duration: .5
9926 });
9927 </code></pre>
9928          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9929          * @param {Object} options (optional) Object literal with any of the Fx config options
9930          * @return {Roo.Element} The Element
9931          */
9932     slideIn : function(anchor, o){
9933         var el = this.getFxEl();
9934         o = o || {};
9935
9936         el.queueFx(o, function(){
9937
9938             anchor = anchor || "t";
9939
9940             // fix display to visibility
9941             this.fixDisplay();
9942
9943             // restore values after effect
9944             var r = this.getFxRestore();
9945             var b = this.getBox();
9946             // fixed size for slide
9947             this.setSize(b);
9948
9949             // wrap if needed
9950             var wrap = this.fxWrap(r.pos, o, "hidden");
9951
9952             var st = this.dom.style;
9953             st.visibility = "visible";
9954             st.position = "absolute";
9955
9956             // clear out temp styles after slide and unwrap
9957             var after = function(){
9958                 el.fxUnwrap(wrap, r.pos, o);
9959                 st.width = r.width;
9960                 st.height = r.height;
9961                 el.afterFx(o);
9962             };
9963             // time to calc the positions
9964             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9965
9966             switch(anchor.toLowerCase()){
9967                 case "t":
9968                     wrap.setSize(b.width, 0);
9969                     st.left = st.bottom = "0";
9970                     a = {height: bh};
9971                 break;
9972                 case "l":
9973                     wrap.setSize(0, b.height);
9974                     st.right = st.top = "0";
9975                     a = {width: bw};
9976                 break;
9977                 case "r":
9978                     wrap.setSize(0, b.height);
9979                     wrap.setX(b.right);
9980                     st.left = st.top = "0";
9981                     a = {width: bw, points: pt};
9982                 break;
9983                 case "b":
9984                     wrap.setSize(b.width, 0);
9985                     wrap.setY(b.bottom);
9986                     st.left = st.top = "0";
9987                     a = {height: bh, points: pt};
9988                 break;
9989                 case "tl":
9990                     wrap.setSize(0, 0);
9991                     st.right = st.bottom = "0";
9992                     a = {width: bw, height: bh};
9993                 break;
9994                 case "bl":
9995                     wrap.setSize(0, 0);
9996                     wrap.setY(b.y+b.height);
9997                     st.right = st.top = "0";
9998                     a = {width: bw, height: bh, points: pt};
9999                 break;
10000                 case "br":
10001                     wrap.setSize(0, 0);
10002                     wrap.setXY([b.right, b.bottom]);
10003                     st.left = st.top = "0";
10004                     a = {width: bw, height: bh, points: pt};
10005                 break;
10006                 case "tr":
10007                     wrap.setSize(0, 0);
10008                     wrap.setX(b.x+b.width);
10009                     st.left = st.bottom = "0";
10010                     a = {width: bw, height: bh, points: pt};
10011                 break;
10012             }
10013             this.dom.style.visibility = "visible";
10014             wrap.show();
10015
10016             arguments.callee.anim = wrap.fxanim(a,
10017                 o,
10018                 'motion',
10019                 .5,
10020                 'easeOut', after);
10021         });
10022         return this;
10023     },
10024     
10025         /**
10026          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10027          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10028          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10029          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10030          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10031          * Usage:
10032          *<pre><code>
10033 // default: slide the element out to the top
10034 el.slideOut();
10035
10036 // custom: slide the element out to the right with a 2-second duration
10037 el.slideOut('r', { duration: 2 });
10038
10039 // common config options shown with default values
10040 el.slideOut('t', {
10041     easing: 'easeOut',
10042     duration: .5,
10043     remove: false,
10044     useDisplay: false
10045 });
10046 </code></pre>
10047          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10048          * @param {Object} options (optional) Object literal with any of the Fx config options
10049          * @return {Roo.Element} The Element
10050          */
10051     slideOut : function(anchor, o){
10052         var el = this.getFxEl();
10053         o = o || {};
10054
10055         el.queueFx(o, function(){
10056
10057             anchor = anchor || "t";
10058
10059             // restore values after effect
10060             var r = this.getFxRestore();
10061             
10062             var b = this.getBox();
10063             // fixed size for slide
10064             this.setSize(b);
10065
10066             // wrap if needed
10067             var wrap = this.fxWrap(r.pos, o, "visible");
10068
10069             var st = this.dom.style;
10070             st.visibility = "visible";
10071             st.position = "absolute";
10072
10073             wrap.setSize(b);
10074
10075             var after = function(){
10076                 if(o.useDisplay){
10077                     el.setDisplayed(false);
10078                 }else{
10079                     el.hide();
10080                 }
10081
10082                 el.fxUnwrap(wrap, r.pos, o);
10083
10084                 st.width = r.width;
10085                 st.height = r.height;
10086
10087                 el.afterFx(o);
10088             };
10089
10090             var a, zero = {to: 0};
10091             switch(anchor.toLowerCase()){
10092                 case "t":
10093                     st.left = st.bottom = "0";
10094                     a = {height: zero};
10095                 break;
10096                 case "l":
10097                     st.right = st.top = "0";
10098                     a = {width: zero};
10099                 break;
10100                 case "r":
10101                     st.left = st.top = "0";
10102                     a = {width: zero, points: {to:[b.right, b.y]}};
10103                 break;
10104                 case "b":
10105                     st.left = st.top = "0";
10106                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10107                 break;
10108                 case "tl":
10109                     st.right = st.bottom = "0";
10110                     a = {width: zero, height: zero};
10111                 break;
10112                 case "bl":
10113                     st.right = st.top = "0";
10114                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10115                 break;
10116                 case "br":
10117                     st.left = st.top = "0";
10118                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10119                 break;
10120                 case "tr":
10121                     st.left = st.bottom = "0";
10122                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10123                 break;
10124             }
10125
10126             arguments.callee.anim = wrap.fxanim(a,
10127                 o,
10128                 'motion',
10129                 .5,
10130                 "easeOut", after);
10131         });
10132         return this;
10133     },
10134
10135         /**
10136          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10137          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10138          * The element must be removed from the DOM using the 'remove' config option if desired.
10139          * Usage:
10140          *<pre><code>
10141 // default
10142 el.puff();
10143
10144 // common config options shown with default values
10145 el.puff({
10146     easing: 'easeOut',
10147     duration: .5,
10148     remove: false,
10149     useDisplay: false
10150 });
10151 </code></pre>
10152          * @param {Object} options (optional) Object literal with any of the Fx config options
10153          * @return {Roo.Element} The Element
10154          */
10155     puff : function(o){
10156         var el = this.getFxEl();
10157         o = o || {};
10158
10159         el.queueFx(o, function(){
10160             this.clearOpacity();
10161             this.show();
10162
10163             // restore values after effect
10164             var r = this.getFxRestore();
10165             var st = this.dom.style;
10166
10167             var after = function(){
10168                 if(o.useDisplay){
10169                     el.setDisplayed(false);
10170                 }else{
10171                     el.hide();
10172                 }
10173
10174                 el.clearOpacity();
10175
10176                 el.setPositioning(r.pos);
10177                 st.width = r.width;
10178                 st.height = r.height;
10179                 st.fontSize = '';
10180                 el.afterFx(o);
10181             };
10182
10183             var width = this.getWidth();
10184             var height = this.getHeight();
10185
10186             arguments.callee.anim = this.fxanim({
10187                     width : {to: this.adjustWidth(width * 2)},
10188                     height : {to: this.adjustHeight(height * 2)},
10189                     points : {by: [-(width * .5), -(height * .5)]},
10190                     opacity : {to: 0},
10191                     fontSize: {to:200, unit: "%"}
10192                 },
10193                 o,
10194                 'motion',
10195                 .5,
10196                 "easeOut", after);
10197         });
10198         return this;
10199     },
10200
10201         /**
10202          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10203          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10204          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10205          * Usage:
10206          *<pre><code>
10207 // default
10208 el.switchOff();
10209
10210 // all config options shown with default values
10211 el.switchOff({
10212     easing: 'easeIn',
10213     duration: .3,
10214     remove: false,
10215     useDisplay: false
10216 });
10217 </code></pre>
10218          * @param {Object} options (optional) Object literal with any of the Fx config options
10219          * @return {Roo.Element} The Element
10220          */
10221     switchOff : function(o){
10222         var el = this.getFxEl();
10223         o = o || {};
10224
10225         el.queueFx(o, function(){
10226             this.clearOpacity();
10227             this.clip();
10228
10229             // restore values after effect
10230             var r = this.getFxRestore();
10231             var st = this.dom.style;
10232
10233             var after = function(){
10234                 if(o.useDisplay){
10235                     el.setDisplayed(false);
10236                 }else{
10237                     el.hide();
10238                 }
10239
10240                 el.clearOpacity();
10241                 el.setPositioning(r.pos);
10242                 st.width = r.width;
10243                 st.height = r.height;
10244
10245                 el.afterFx(o);
10246             };
10247
10248             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10249                 this.clearOpacity();
10250                 (function(){
10251                     this.fxanim({
10252                         height:{to:1},
10253                         points:{by:[0, this.getHeight() * .5]}
10254                     }, o, 'motion', 0.3, 'easeIn', after);
10255                 }).defer(100, this);
10256             });
10257         });
10258         return this;
10259     },
10260
10261     /**
10262      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10263      * changed using the "attr" config option) and then fading back to the original color. If no original
10264      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10265      * Usage:
10266 <pre><code>
10267 // default: highlight background to yellow
10268 el.highlight();
10269
10270 // custom: highlight foreground text to blue for 2 seconds
10271 el.highlight("0000ff", { attr: 'color', duration: 2 });
10272
10273 // common config options shown with default values
10274 el.highlight("ffff9c", {
10275     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10276     endColor: (current color) or "ffffff",
10277     easing: 'easeIn',
10278     duration: 1
10279 });
10280 </code></pre>
10281      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10282      * @param {Object} options (optional) Object literal with any of the Fx config options
10283      * @return {Roo.Element} The Element
10284      */ 
10285     highlight : function(color, o){
10286         var el = this.getFxEl();
10287         o = o || {};
10288
10289         el.queueFx(o, function(){
10290             color = color || "ffff9c";
10291             attr = o.attr || "backgroundColor";
10292
10293             this.clearOpacity();
10294             this.show();
10295
10296             var origColor = this.getColor(attr);
10297             var restoreColor = this.dom.style[attr];
10298             endColor = (o.endColor || origColor) || "ffffff";
10299
10300             var after = function(){
10301                 el.dom.style[attr] = restoreColor;
10302                 el.afterFx(o);
10303             };
10304
10305             var a = {};
10306             a[attr] = {from: color, to: endColor};
10307             arguments.callee.anim = this.fxanim(a,
10308                 o,
10309                 'color',
10310                 1,
10311                 'easeIn', after);
10312         });
10313         return this;
10314     },
10315
10316    /**
10317     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10318     * Usage:
10319 <pre><code>
10320 // default: a single light blue ripple
10321 el.frame();
10322
10323 // custom: 3 red ripples lasting 3 seconds total
10324 el.frame("ff0000", 3, { duration: 3 });
10325
10326 // common config options shown with default values
10327 el.frame("C3DAF9", 1, {
10328     duration: 1 //duration of entire animation (not each individual ripple)
10329     // Note: Easing is not configurable and will be ignored if included
10330 });
10331 </code></pre>
10332     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10333     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10334     * @param {Object} options (optional) Object literal with any of the Fx config options
10335     * @return {Roo.Element} The Element
10336     */
10337     frame : function(color, count, o){
10338         var el = this.getFxEl();
10339         o = o || {};
10340
10341         el.queueFx(o, function(){
10342             color = color || "#C3DAF9";
10343             if(color.length == 6){
10344                 color = "#" + color;
10345             }
10346             count = count || 1;
10347             duration = o.duration || 1;
10348             this.show();
10349
10350             var b = this.getBox();
10351             var animFn = function(){
10352                 var proxy = this.createProxy({
10353
10354                      style:{
10355                         visbility:"hidden",
10356                         position:"absolute",
10357                         "z-index":"35000", // yee haw
10358                         border:"0px solid " + color
10359                      }
10360                   });
10361                 var scale = Roo.isBorderBox ? 2 : 1;
10362                 proxy.animate({
10363                     top:{from:b.y, to:b.y - 20},
10364                     left:{from:b.x, to:b.x - 20},
10365                     borderWidth:{from:0, to:10},
10366                     opacity:{from:1, to:0},
10367                     height:{from:b.height, to:(b.height + (20*scale))},
10368                     width:{from:b.width, to:(b.width + (20*scale))}
10369                 }, duration, function(){
10370                     proxy.remove();
10371                 });
10372                 if(--count > 0){
10373                      animFn.defer((duration/2)*1000, this);
10374                 }else{
10375                     el.afterFx(o);
10376                 }
10377             };
10378             animFn.call(this);
10379         });
10380         return this;
10381     },
10382
10383    /**
10384     * Creates a pause before any subsequent queued effects begin.  If there are
10385     * no effects queued after the pause it will have no effect.
10386     * Usage:
10387 <pre><code>
10388 el.pause(1);
10389 </code></pre>
10390     * @param {Number} seconds The length of time to pause (in seconds)
10391     * @return {Roo.Element} The Element
10392     */
10393     pause : function(seconds){
10394         var el = this.getFxEl();
10395         var o = {};
10396
10397         el.queueFx(o, function(){
10398             setTimeout(function(){
10399                 el.afterFx(o);
10400             }, seconds * 1000);
10401         });
10402         return this;
10403     },
10404
10405    /**
10406     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10407     * using the "endOpacity" config option.
10408     * Usage:
10409 <pre><code>
10410 // default: fade in from opacity 0 to 100%
10411 el.fadeIn();
10412
10413 // custom: fade in from opacity 0 to 75% over 2 seconds
10414 el.fadeIn({ endOpacity: .75, duration: 2});
10415
10416 // common config options shown with default values
10417 el.fadeIn({
10418     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10419     easing: 'easeOut',
10420     duration: .5
10421 });
10422 </code></pre>
10423     * @param {Object} options (optional) Object literal with any of the Fx config options
10424     * @return {Roo.Element} The Element
10425     */
10426     fadeIn : function(o){
10427         var el = this.getFxEl();
10428         o = o || {};
10429         el.queueFx(o, function(){
10430             this.setOpacity(0);
10431             this.fixDisplay();
10432             this.dom.style.visibility = 'visible';
10433             var to = o.endOpacity || 1;
10434             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10435                 o, null, .5, "easeOut", function(){
10436                 if(to == 1){
10437                     this.clearOpacity();
10438                 }
10439                 el.afterFx(o);
10440             });
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10447     * using the "endOpacity" config option.
10448     * Usage:
10449 <pre><code>
10450 // default: fade out from the element's current opacity to 0
10451 el.fadeOut();
10452
10453 // custom: fade out from the element's current opacity to 25% over 2 seconds
10454 el.fadeOut({ endOpacity: .25, duration: 2});
10455
10456 // common config options shown with default values
10457 el.fadeOut({
10458     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10459     easing: 'easeOut',
10460     duration: .5
10461     remove: false,
10462     useDisplay: false
10463 });
10464 </code></pre>
10465     * @param {Object} options (optional) Object literal with any of the Fx config options
10466     * @return {Roo.Element} The Element
10467     */
10468     fadeOut : function(o){
10469         var el = this.getFxEl();
10470         o = o || {};
10471         el.queueFx(o, function(){
10472             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10473                 o, null, .5, "easeOut", function(){
10474                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10475                      this.dom.style.display = "none";
10476                 }else{
10477                      this.dom.style.visibility = "hidden";
10478                 }
10479                 this.clearOpacity();
10480                 el.afterFx(o);
10481             });
10482         });
10483         return this;
10484     },
10485
10486    /**
10487     * Animates the transition of an element's dimensions from a starting height/width
10488     * to an ending height/width.
10489     * Usage:
10490 <pre><code>
10491 // change height and width to 100x100 pixels
10492 el.scale(100, 100);
10493
10494 // common config options shown with default values.  The height and width will default to
10495 // the element's existing values if passed as null.
10496 el.scale(
10497     [element's width],
10498     [element's height], {
10499     easing: 'easeOut',
10500     duration: .35
10501 });
10502 </code></pre>
10503     * @param {Number} width  The new width (pass undefined to keep the original width)
10504     * @param {Number} height  The new height (pass undefined to keep the original height)
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     scale : function(w, h, o){
10509         this.shift(Roo.apply({}, o, {
10510             width: w,
10511             height: h
10512         }));
10513         return this;
10514     },
10515
10516    /**
10517     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10518     * Any of these properties not specified in the config object will not be changed.  This effect 
10519     * requires that at least one new dimension, position or opacity setting must be passed in on
10520     * the config object in order for the function to have any effect.
10521     * Usage:
10522 <pre><code>
10523 // slide the element horizontally to x position 200 while changing the height and opacity
10524 el.shift({ x: 200, height: 50, opacity: .8 });
10525
10526 // common config options shown with default values.
10527 el.shift({
10528     width: [element's width],
10529     height: [element's height],
10530     x: [element's x position],
10531     y: [element's y position],
10532     opacity: [element's opacity],
10533     easing: 'easeOut',
10534     duration: .35
10535 });
10536 </code></pre>
10537     * @param {Object} options  Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     shift : function(o){
10541         var el = this.getFxEl();
10542         o = o || {};
10543         el.queueFx(o, function(){
10544             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10545             if(w !== undefined){
10546                 a.width = {to: this.adjustWidth(w)};
10547             }
10548             if(h !== undefined){
10549                 a.height = {to: this.adjustHeight(h)};
10550             }
10551             if(x !== undefined || y !== undefined){
10552                 a.points = {to: [
10553                     x !== undefined ? x : this.getX(),
10554                     y !== undefined ? y : this.getY()
10555                 ]};
10556             }
10557             if(op !== undefined){
10558                 a.opacity = {to: op};
10559             }
10560             if(o.xy !== undefined){
10561                 a.points = {to: o.xy};
10562             }
10563             arguments.callee.anim = this.fxanim(a,
10564                 o, 'motion', .35, "easeOut", function(){
10565                 el.afterFx(o);
10566             });
10567         });
10568         return this;
10569     },
10570
10571         /**
10572          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10573          * ending point of the effect.
10574          * Usage:
10575          *<pre><code>
10576 // default: slide the element downward while fading out
10577 el.ghost();
10578
10579 // custom: slide the element out to the right with a 2-second duration
10580 el.ghost('r', { duration: 2 });
10581
10582 // common config options shown with default values
10583 el.ghost('b', {
10584     easing: 'easeOut',
10585     duration: .5
10586     remove: false,
10587     useDisplay: false
10588 });
10589 </code></pre>
10590          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10591          * @param {Object} options (optional) Object literal with any of the Fx config options
10592          * @return {Roo.Element} The Element
10593          */
10594     ghost : function(anchor, o){
10595         var el = this.getFxEl();
10596         o = o || {};
10597
10598         el.queueFx(o, function(){
10599             anchor = anchor || "b";
10600
10601             // restore values after effect
10602             var r = this.getFxRestore();
10603             var w = this.getWidth(),
10604                 h = this.getHeight();
10605
10606             var st = this.dom.style;
10607
10608             var after = function(){
10609                 if(o.useDisplay){
10610                     el.setDisplayed(false);
10611                 }else{
10612                     el.hide();
10613                 }
10614
10615                 el.clearOpacity();
10616                 el.setPositioning(r.pos);
10617                 st.width = r.width;
10618                 st.height = r.height;
10619
10620                 el.afterFx(o);
10621             };
10622
10623             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10624             switch(anchor.toLowerCase()){
10625                 case "t":
10626                     pt.by = [0, -h];
10627                 break;
10628                 case "l":
10629                     pt.by = [-w, 0];
10630                 break;
10631                 case "r":
10632                     pt.by = [w, 0];
10633                 break;
10634                 case "b":
10635                     pt.by = [0, h];
10636                 break;
10637                 case "tl":
10638                     pt.by = [-w, -h];
10639                 break;
10640                 case "bl":
10641                     pt.by = [-w, h];
10642                 break;
10643                 case "br":
10644                     pt.by = [w, h];
10645                 break;
10646                 case "tr":
10647                     pt.by = [w, -h];
10648                 break;
10649             }
10650
10651             arguments.callee.anim = this.fxanim(a,
10652                 o,
10653                 'motion',
10654                 .5,
10655                 "easeOut", after);
10656         });
10657         return this;
10658     },
10659
10660         /**
10661          * Ensures that all effects queued after syncFx is called on the element are
10662          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10663          * @return {Roo.Element} The Element
10664          */
10665     syncFx : function(){
10666         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10667             block : false,
10668             concurrent : true,
10669             stopFx : false
10670         });
10671         return this;
10672     },
10673
10674         /**
10675          * Ensures that all effects queued after sequenceFx is called on the element are
10676          * run in sequence.  This is the opposite of {@link #syncFx}.
10677          * @return {Roo.Element} The Element
10678          */
10679     sequenceFx : function(){
10680         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10681             block : false,
10682             concurrent : false,
10683             stopFx : false
10684         });
10685         return this;
10686     },
10687
10688         /* @private */
10689     nextFx : function(){
10690         var ef = this.fxQueue[0];
10691         if(ef){
10692             ef.call(this);
10693         }
10694     },
10695
10696         /**
10697          * Returns true if the element has any effects actively running or queued, else returns false.
10698          * @return {Boolean} True if element has active effects, else false
10699          */
10700     hasActiveFx : function(){
10701         return this.fxQueue && this.fxQueue[0];
10702     },
10703
10704         /**
10705          * Stops any running effects and clears the element's internal effects queue if it contains
10706          * any additional effects that haven't started yet.
10707          * @return {Roo.Element} The Element
10708          */
10709     stopFx : function(){
10710         if(this.hasActiveFx()){
10711             var cur = this.fxQueue[0];
10712             if(cur && cur.anim && cur.anim.isAnimated()){
10713                 this.fxQueue = [cur]; // clear out others
10714                 cur.anim.stop(true);
10715             }
10716         }
10717         return this;
10718     },
10719
10720         /* @private */
10721     beforeFx : function(o){
10722         if(this.hasActiveFx() && !o.concurrent){
10723            if(o.stopFx){
10724                this.stopFx();
10725                return true;
10726            }
10727            return false;
10728         }
10729         return true;
10730     },
10731
10732         /**
10733          * Returns true if the element is currently blocking so that no other effect can be queued
10734          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10735          * used to ensure that an effect initiated by a user action runs to completion prior to the
10736          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10737          * @return {Boolean} True if blocking, else false
10738          */
10739     hasFxBlock : function(){
10740         var q = this.fxQueue;
10741         return q && q[0] && q[0].block;
10742     },
10743
10744         /* @private */
10745     queueFx : function(o, fn){
10746         if(!this.fxQueue){
10747             this.fxQueue = [];
10748         }
10749         if(!this.hasFxBlock()){
10750             Roo.applyIf(o, this.fxDefaults);
10751             if(!o.concurrent){
10752                 var run = this.beforeFx(o);
10753                 fn.block = o.block;
10754                 this.fxQueue.push(fn);
10755                 if(run){
10756                     this.nextFx();
10757                 }
10758             }else{
10759                 fn.call(this);
10760             }
10761         }
10762         return this;
10763     },
10764
10765         /* @private */
10766     fxWrap : function(pos, o, vis){
10767         var wrap;
10768         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10769             var wrapXY;
10770             if(o.fixPosition){
10771                 wrapXY = this.getXY();
10772             }
10773             var div = document.createElement("div");
10774             div.style.visibility = vis;
10775             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10776             wrap.setPositioning(pos);
10777             if(wrap.getStyle("position") == "static"){
10778                 wrap.position("relative");
10779             }
10780             this.clearPositioning('auto');
10781             wrap.clip();
10782             wrap.dom.appendChild(this.dom);
10783             if(wrapXY){
10784                 wrap.setXY(wrapXY);
10785             }
10786         }
10787         return wrap;
10788     },
10789
10790         /* @private */
10791     fxUnwrap : function(wrap, pos, o){
10792         this.clearPositioning();
10793         this.setPositioning(pos);
10794         if(!o.wrap){
10795             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10796             wrap.remove();
10797         }
10798     },
10799
10800         /* @private */
10801     getFxRestore : function(){
10802         var st = this.dom.style;
10803         return {pos: this.getPositioning(), width: st.width, height : st.height};
10804     },
10805
10806         /* @private */
10807     afterFx : function(o){
10808         if(o.afterStyle){
10809             this.applyStyles(o.afterStyle);
10810         }
10811         if(o.afterCls){
10812             this.addClass(o.afterCls);
10813         }
10814         if(o.remove === true){
10815             this.remove();
10816         }
10817         Roo.callback(o.callback, o.scope, [this]);
10818         if(!o.concurrent){
10819             this.fxQueue.shift();
10820             this.nextFx();
10821         }
10822     },
10823
10824         /* @private */
10825     getFxEl : function(){ // support for composite element fx
10826         return Roo.get(this.dom);
10827     },
10828
10829         /* @private */
10830     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10831         animType = animType || 'run';
10832         opt = opt || {};
10833         var anim = Roo.lib.Anim[animType](
10834             this.dom, args,
10835             (opt.duration || defaultDur) || .35,
10836             (opt.easing || defaultEase) || 'easeOut',
10837             function(){
10838                 Roo.callback(cb, this);
10839             },
10840             this
10841         );
10842         opt.anim = anim;
10843         return anim;
10844     }
10845 };
10846
10847 // backwords compat
10848 Roo.Fx.resize = Roo.Fx.scale;
10849
10850 //When included, Roo.Fx is automatically applied to Element so that all basic
10851 //effects are available directly via the Element API
10852 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10853  * Based on:
10854  * Ext JS Library 1.1.1
10855  * Copyright(c) 2006-2007, Ext JS, LLC.
10856  *
10857  * Originally Released Under LGPL - original licence link has changed is not relivant.
10858  *
10859  * Fork - LGPL
10860  * <script type="text/javascript">
10861  */
10862
10863
10864 /**
10865  * @class Roo.CompositeElement
10866  * Standard composite class. Creates a Roo.Element for every element in the collection.
10867  * <br><br>
10868  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10869  * actions will be performed on all the elements in this collection.</b>
10870  * <br><br>
10871  * All methods return <i>this</i> and can be chained.
10872  <pre><code>
10873  var els = Roo.select("#some-el div.some-class", true);
10874  // or select directly from an existing element
10875  var el = Roo.get('some-el');
10876  el.select('div.some-class', true);
10877
10878  els.setWidth(100); // all elements become 100 width
10879  els.hide(true); // all elements fade out and hide
10880  // or
10881  els.setWidth(100).hide(true);
10882  </code></pre>
10883  */
10884 Roo.CompositeElement = function(els){
10885     this.elements = [];
10886     this.addElements(els);
10887 };
10888 Roo.CompositeElement.prototype = {
10889     isComposite: true,
10890     addElements : function(els){
10891         if(!els) return this;
10892         if(typeof els == "string"){
10893             els = Roo.Element.selectorFunction(els);
10894         }
10895         var yels = this.elements;
10896         var index = yels.length-1;
10897         for(var i = 0, len = els.length; i < len; i++) {
10898                 yels[++index] = Roo.get(els[i]);
10899         }
10900         return this;
10901     },
10902
10903     /**
10904     * Clears this composite and adds the elements returned by the passed selector.
10905     * @param {String/Array} els A string CSS selector, an array of elements or an element
10906     * @return {CompositeElement} this
10907     */
10908     fill : function(els){
10909         this.elements = [];
10910         this.add(els);
10911         return this;
10912     },
10913
10914     /**
10915     * Filters this composite to only elements that match the passed selector.
10916     * @param {String} selector A string CSS selector
10917     * @return {CompositeElement} this
10918     */
10919     filter : function(selector){
10920         var els = [];
10921         this.each(function(el){
10922             if(el.is(selector)){
10923                 els[els.length] = el.dom;
10924             }
10925         });
10926         this.fill(els);
10927         return this;
10928     },
10929
10930     invoke : function(fn, args){
10931         var els = this.elements;
10932         for(var i = 0, len = els.length; i < len; i++) {
10933                 Roo.Element.prototype[fn].apply(els[i], args);
10934         }
10935         return this;
10936     },
10937     /**
10938     * Adds elements to this composite.
10939     * @param {String/Array} els A string CSS selector, an array of elements or an element
10940     * @return {CompositeElement} this
10941     */
10942     add : function(els){
10943         if(typeof els == "string"){
10944             this.addElements(Roo.Element.selectorFunction(els));
10945         }else if(els.length !== undefined){
10946             this.addElements(els);
10947         }else{
10948             this.addElements([els]);
10949         }
10950         return this;
10951     },
10952     /**
10953     * Calls the passed function passing (el, this, index) for each element in this composite.
10954     * @param {Function} fn The function to call
10955     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10956     * @return {CompositeElement} this
10957     */
10958     each : function(fn, scope){
10959         var els = this.elements;
10960         for(var i = 0, len = els.length; i < len; i++){
10961             if(fn.call(scope || els[i], els[i], this, i) === false) {
10962                 break;
10963             }
10964         }
10965         return this;
10966     },
10967
10968     /**
10969      * Returns the Element object at the specified index
10970      * @param {Number} index
10971      * @return {Roo.Element}
10972      */
10973     item : function(index){
10974         return this.elements[index] || null;
10975     },
10976
10977     /**
10978      * Returns the first Element
10979      * @return {Roo.Element}
10980      */
10981     first : function(){
10982         return this.item(0);
10983     },
10984
10985     /**
10986      * Returns the last Element
10987      * @return {Roo.Element}
10988      */
10989     last : function(){
10990         return this.item(this.elements.length-1);
10991     },
10992
10993     /**
10994      * Returns the number of elements in this composite
10995      * @return Number
10996      */
10997     getCount : function(){
10998         return this.elements.length;
10999     },
11000
11001     /**
11002      * Returns true if this composite contains the passed element
11003      * @return Boolean
11004      */
11005     contains : function(el){
11006         return this.indexOf(el) !== -1;
11007     },
11008
11009     /**
11010      * Returns true if this composite contains the passed element
11011      * @return Boolean
11012      */
11013     indexOf : function(el){
11014         return this.elements.indexOf(Roo.get(el));
11015     },
11016
11017
11018     /**
11019     * Removes the specified element(s).
11020     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11021     * or an array of any of those.
11022     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11023     * @return {CompositeElement} this
11024     */
11025     removeElement : function(el, removeDom){
11026         if(el instanceof Array){
11027             for(var i = 0, len = el.length; i < len; i++){
11028                 this.removeElement(el[i]);
11029             }
11030             return this;
11031         }
11032         var index = typeof el == 'number' ? el : this.indexOf(el);
11033         if(index !== -1){
11034             if(removeDom){
11035                 var d = this.elements[index];
11036                 if(d.dom){
11037                     d.remove();
11038                 }else{
11039                     d.parentNode.removeChild(d);
11040                 }
11041             }
11042             this.elements.splice(index, 1);
11043         }
11044         return this;
11045     },
11046
11047     /**
11048     * Replaces the specified element with the passed element.
11049     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11050     * to replace.
11051     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11052     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11053     * @return {CompositeElement} this
11054     */
11055     replaceElement : function(el, replacement, domReplace){
11056         var index = typeof el == 'number' ? el : this.indexOf(el);
11057         if(index !== -1){
11058             if(domReplace){
11059                 this.elements[index].replaceWith(replacement);
11060             }else{
11061                 this.elements.splice(index, 1, Roo.get(replacement))
11062             }
11063         }
11064         return this;
11065     },
11066
11067     /**
11068      * Removes all elements.
11069      */
11070     clear : function(){
11071         this.elements = [];
11072     }
11073 };
11074 (function(){
11075     Roo.CompositeElement.createCall = function(proto, fnName){
11076         if(!proto[fnName]){
11077             proto[fnName] = function(){
11078                 return this.invoke(fnName, arguments);
11079             };
11080         }
11081     };
11082     for(var fnName in Roo.Element.prototype){
11083         if(typeof Roo.Element.prototype[fnName] == "function"){
11084             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11085         }
11086     };
11087 })();
11088 /*
11089  * Based on:
11090  * Ext JS Library 1.1.1
11091  * Copyright(c) 2006-2007, Ext JS, LLC.
11092  *
11093  * Originally Released Under LGPL - original licence link has changed is not relivant.
11094  *
11095  * Fork - LGPL
11096  * <script type="text/javascript">
11097  */
11098
11099 /**
11100  * @class Roo.CompositeElementLite
11101  * @extends Roo.CompositeElement
11102  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11103  <pre><code>
11104  var els = Roo.select("#some-el div.some-class");
11105  // or select directly from an existing element
11106  var el = Roo.get('some-el');
11107  el.select('div.some-class');
11108
11109  els.setWidth(100); // all elements become 100 width
11110  els.hide(true); // all elements fade out and hide
11111  // or
11112  els.setWidth(100).hide(true);
11113  </code></pre><br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  */
11117 Roo.CompositeElementLite = function(els){
11118     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11119     this.el = new Roo.Element.Flyweight();
11120 };
11121 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11122     addElements : function(els){
11123         if(els){
11124             if(els instanceof Array){
11125                 this.elements = this.elements.concat(els);
11126             }else{
11127                 var yels = this.elements;
11128                 var index = yels.length-1;
11129                 for(var i = 0, len = els.length; i < len; i++) {
11130                     yels[++index] = els[i];
11131                 }
11132             }
11133         }
11134         return this;
11135     },
11136     invoke : function(fn, args){
11137         var els = this.elements;
11138         var el = this.el;
11139         for(var i = 0, len = els.length; i < len; i++) {
11140             el.dom = els[i];
11141                 Roo.Element.prototype[fn].apply(el, args);
11142         }
11143         return this;
11144     },
11145     /**
11146      * Returns a flyweight Element of the dom element object at the specified index
11147      * @param {Number} index
11148      * @return {Roo.Element}
11149      */
11150     item : function(index){
11151         if(!this.elements[index]){
11152             return null;
11153         }
11154         this.el.dom = this.elements[index];
11155         return this.el;
11156     },
11157
11158     // fixes scope with flyweight
11159     addListener : function(eventName, handler, scope, opt){
11160         var els = this.elements;
11161         for(var i = 0, len = els.length; i < len; i++) {
11162             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11163         }
11164         return this;
11165     },
11166
11167     /**
11168     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11169     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11170     * a reference to the dom node, use el.dom.</b>
11171     * @param {Function} fn The function to call
11172     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11173     * @return {CompositeElement} this
11174     */
11175     each : function(fn, scope){
11176         var els = this.elements;
11177         var el = this.el;
11178         for(var i = 0, len = els.length; i < len; i++){
11179             el.dom = els[i];
11180                 if(fn.call(scope || el, el, this, i) === false){
11181                 break;
11182             }
11183         }
11184         return this;
11185     },
11186
11187     indexOf : function(el){
11188         return this.elements.indexOf(Roo.getDom(el));
11189     },
11190
11191     replaceElement : function(el, replacement, domReplace){
11192         var index = typeof el == 'number' ? el : this.indexOf(el);
11193         if(index !== -1){
11194             replacement = Roo.getDom(replacement);
11195             if(domReplace){
11196                 var d = this.elements[index];
11197                 d.parentNode.insertBefore(replacement, d);
11198                 d.parentNode.removeChild(d);
11199             }
11200             this.elements.splice(index, 1, replacement);
11201         }
11202         return this;
11203     }
11204 });
11205 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11206
11207 /*
11208  * Based on:
11209  * Ext JS Library 1.1.1
11210  * Copyright(c) 2006-2007, Ext JS, LLC.
11211  *
11212  * Originally Released Under LGPL - original licence link has changed is not relivant.
11213  *
11214  * Fork - LGPL
11215  * <script type="text/javascript">
11216  */
11217
11218  
11219
11220 /**
11221  * @class Roo.data.Connection
11222  * @extends Roo.util.Observable
11223  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11224  * either to a configured URL, or to a URL specified at request time.<br><br>
11225  * <p>
11226  * Requests made by this class are asynchronous, and will return immediately. No data from
11227  * the server will be available to the statement immediately following the {@link #request} call.
11228  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11229  * <p>
11230  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11231  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11232  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11233  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11234  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11235  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11236  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11237  * standard DOM methods.
11238  * @constructor
11239  * @param {Object} config a configuration object.
11240  */
11241 Roo.data.Connection = function(config){
11242     Roo.apply(this, config);
11243     this.addEvents({
11244         /**
11245          * @event beforerequest
11246          * Fires before a network request is made to retrieve a data object.
11247          * @param {Connection} conn This Connection object.
11248          * @param {Object} options The options config object passed to the {@link #request} method.
11249          */
11250         "beforerequest" : true,
11251         /**
11252          * @event requestcomplete
11253          * Fires if the request was successfully completed.
11254          * @param {Connection} conn This Connection object.
11255          * @param {Object} response The XHR object containing the response data.
11256          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11257          * @param {Object} options The options config object passed to the {@link #request} method.
11258          */
11259         "requestcomplete" : true,
11260         /**
11261          * @event requestexception
11262          * Fires if an error HTTP status was returned from the server.
11263          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11264          * @param {Connection} conn This Connection object.
11265          * @param {Object} response The XHR object containing the response data.
11266          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11267          * @param {Object} options The options config object passed to the {@link #request} method.
11268          */
11269         "requestexception" : true
11270     });
11271     Roo.data.Connection.superclass.constructor.call(this);
11272 };
11273
11274 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11275     /**
11276      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11277      */
11278     /**
11279      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11280      * extra parameters to each request made by this object. (defaults to undefined)
11281      */
11282     /**
11283      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11284      *  to each request made by this object. (defaults to undefined)
11285      */
11286     /**
11287      * @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)
11288      */
11289     /**
11290      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11291      */
11292     timeout : 30000,
11293     /**
11294      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11295      * @type Boolean
11296      */
11297     autoAbort:false,
11298
11299     /**
11300      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11301      * @type Boolean
11302      */
11303     disableCaching: true,
11304
11305     /**
11306      * Sends an HTTP request to a remote server.
11307      * @param {Object} options An object which may contain the following properties:<ul>
11308      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11309      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11310      * request, a url encoded string or a function to call to get either.</li>
11311      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11312      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11313      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11314      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11315      * <li>options {Object} The parameter to the request call.</li>
11316      * <li>success {Boolean} True if the request succeeded.</li>
11317      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11318      * </ul></li>
11319      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11320      * The callback is passed the following parameters:<ul>
11321      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11322      * <li>options {Object} The parameter to the request call.</li>
11323      * </ul></li>
11324      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11325      * The callback is passed the following parameters:<ul>
11326      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11327      * <li>options {Object} The parameter to the request call.</li>
11328      * </ul></li>
11329      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11330      * for the callback function. Defaults to the browser window.</li>
11331      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11332      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11333      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11334      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11335      * params for the post data. Any params will be appended to the URL.</li>
11336      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11337      * </ul>
11338      * @return {Number} transactionId
11339      */
11340     request : function(o){
11341         if(this.fireEvent("beforerequest", this, o) !== false){
11342             var p = o.params;
11343
11344             if(typeof p == "function"){
11345                 p = p.call(o.scope||window, o);
11346             }
11347             if(typeof p == "object"){
11348                 p = Roo.urlEncode(o.params);
11349             }
11350             if(this.extraParams){
11351                 var extras = Roo.urlEncode(this.extraParams);
11352                 p = p ? (p + '&' + extras) : extras;
11353             }
11354
11355             var url = o.url || this.url;
11356             if(typeof url == 'function'){
11357                 url = url.call(o.scope||window, o);
11358             }
11359
11360             if(o.form){
11361                 var form = Roo.getDom(o.form);
11362                 url = url || form.action;
11363
11364                 var enctype = form.getAttribute("enctype");
11365                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11366                     return this.doFormUpload(o, p, url);
11367                 }
11368                 var f = Roo.lib.Ajax.serializeForm(form);
11369                 p = p ? (p + '&' + f) : f;
11370             }
11371
11372             var hs = o.headers;
11373             if(this.defaultHeaders){
11374                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11375                 if(!o.headers){
11376                     o.headers = hs;
11377                 }
11378             }
11379
11380             var cb = {
11381                 success: this.handleResponse,
11382                 failure: this.handleFailure,
11383                 scope: this,
11384                 argument: {options: o},
11385                 timeout : this.timeout
11386             };
11387
11388             var method = o.method||this.method||(p ? "POST" : "GET");
11389
11390             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11391                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11392             }
11393
11394             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11395                 if(o.autoAbort){
11396                     this.abort();
11397                 }
11398             }else if(this.autoAbort !== false){
11399                 this.abort();
11400             }
11401
11402             if((method == 'GET' && p) || o.xmlData){
11403                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11404                 p = '';
11405             }
11406             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11407             return this.transId;
11408         }else{
11409             Roo.callback(o.callback, o.scope, [o, null, null]);
11410             return null;
11411         }
11412     },
11413
11414     /**
11415      * Determine whether this object has a request outstanding.
11416      * @param {Number} transactionId (Optional) defaults to the last transaction
11417      * @return {Boolean} True if there is an outstanding request.
11418      */
11419     isLoading : function(transId){
11420         if(transId){
11421             return Roo.lib.Ajax.isCallInProgress(transId);
11422         }else{
11423             return this.transId ? true : false;
11424         }
11425     },
11426
11427     /**
11428      * Aborts any outstanding request.
11429      * @param {Number} transactionId (Optional) defaults to the last transaction
11430      */
11431     abort : function(transId){
11432         if(transId || this.isLoading()){
11433             Roo.lib.Ajax.abort(transId || this.transId);
11434         }
11435     },
11436
11437     // private
11438     handleResponse : function(response){
11439         this.transId = false;
11440         var options = response.argument.options;
11441         response.argument = options ? options.argument : null;
11442         this.fireEvent("requestcomplete", this, response, options);
11443         Roo.callback(options.success, options.scope, [response, options]);
11444         Roo.callback(options.callback, options.scope, [options, true, response]);
11445     },
11446
11447     // private
11448     handleFailure : function(response, e){
11449         this.transId = false;
11450         var options = response.argument.options;
11451         response.argument = options ? options.argument : null;
11452         this.fireEvent("requestexception", this, response, options, e);
11453         Roo.callback(options.failure, options.scope, [response, options]);
11454         Roo.callback(options.callback, options.scope, [options, false, response]);
11455     },
11456
11457     // private
11458     doFormUpload : function(o, ps, url){
11459         var id = Roo.id();
11460         var frame = document.createElement('iframe');
11461         frame.id = id;
11462         frame.name = id;
11463         frame.className = 'x-hidden';
11464         if(Roo.isIE){
11465             frame.src = Roo.SSL_SECURE_URL;
11466         }
11467         document.body.appendChild(frame);
11468
11469         if(Roo.isIE){
11470            document.frames[id].name = id;
11471         }
11472
11473         var form = Roo.getDom(o.form);
11474         form.target = id;
11475         form.method = 'POST';
11476         form.enctype = form.encoding = 'multipart/form-data';
11477         if(url){
11478             form.action = url;
11479         }
11480
11481         var hiddens, hd;
11482         if(ps){ // add dynamic params
11483             hiddens = [];
11484             ps = Roo.urlDecode(ps, false);
11485             for(var k in ps){
11486                 if(ps.hasOwnProperty(k)){
11487                     hd = document.createElement('input');
11488                     hd.type = 'hidden';
11489                     hd.name = k;
11490                     hd.value = ps[k];
11491                     form.appendChild(hd);
11492                     hiddens.push(hd);
11493                 }
11494             }
11495         }
11496
11497         function cb(){
11498             var r = {  // bogus response object
11499                 responseText : '',
11500                 responseXML : null
11501             };
11502
11503             r.argument = o ? o.argument : null;
11504
11505             try { //
11506                 var doc;
11507                 if(Roo.isIE){
11508                     doc = frame.contentWindow.document;
11509                 }else {
11510                     doc = (frame.contentDocument || window.frames[id].document);
11511                 }
11512                 if(doc && doc.body){
11513                     r.responseText = doc.body.innerHTML;
11514                 }
11515                 if(doc && doc.XMLDocument){
11516                     r.responseXML = doc.XMLDocument;
11517                 }else {
11518                     r.responseXML = doc;
11519                 }
11520             }
11521             catch(e) {
11522                 // ignore
11523             }
11524
11525             Roo.EventManager.removeListener(frame, 'load', cb, this);
11526
11527             this.fireEvent("requestcomplete", this, r, o);
11528             Roo.callback(o.success, o.scope, [r, o]);
11529             Roo.callback(o.callback, o.scope, [o, true, r]);
11530
11531             setTimeout(function(){document.body.removeChild(frame);}, 100);
11532         }
11533
11534         Roo.EventManager.on(frame, 'load', cb, this);
11535         form.submit();
11536
11537         if(hiddens){ // remove dynamic params
11538             for(var i = 0, len = hiddens.length; i < len; i++){
11539                 form.removeChild(hiddens[i]);
11540             }
11541         }
11542     }
11543 });
11544
11545 /**
11546  * @class Roo.Ajax
11547  * @extends Roo.data.Connection
11548  * Global Ajax request class.
11549  *
11550  * @singleton
11551  */
11552 Roo.Ajax = new Roo.data.Connection({
11553     // fix up the docs
11554    /**
11555      * @cfg {String} url @hide
11556      */
11557     /**
11558      * @cfg {Object} extraParams @hide
11559      */
11560     /**
11561      * @cfg {Object} defaultHeaders @hide
11562      */
11563     /**
11564      * @cfg {String} method (Optional) @hide
11565      */
11566     /**
11567      * @cfg {Number} timeout (Optional) @hide
11568      */
11569     /**
11570      * @cfg {Boolean} autoAbort (Optional) @hide
11571      */
11572
11573     /**
11574      * @cfg {Boolean} disableCaching (Optional) @hide
11575      */
11576
11577     /**
11578      * @property  disableCaching
11579      * True to add a unique cache-buster param to GET requests. (defaults to true)
11580      * @type Boolean
11581      */
11582     /**
11583      * @property  url
11584      * The default URL to be used for requests to the server. (defaults to undefined)
11585      * @type String
11586      */
11587     /**
11588      * @property  extraParams
11589      * An object containing properties which are used as
11590      * extra parameters to each request made by this object. (defaults to undefined)
11591      * @type Object
11592      */
11593     /**
11594      * @property  defaultHeaders
11595      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11596      * @type Object
11597      */
11598     /**
11599      * @property  method
11600      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11601      * @type String
11602      */
11603     /**
11604      * @property  timeout
11605      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11606      * @type Number
11607      */
11608
11609     /**
11610      * @property  autoAbort
11611      * Whether a new request should abort any pending requests. (defaults to false)
11612      * @type Boolean
11613      */
11614     autoAbort : false,
11615
11616     /**
11617      * Serialize the passed form into a url encoded string
11618      * @param {String/HTMLElement} form
11619      * @return {String}
11620      */
11621     serializeForm : function(form){
11622         return Roo.lib.Ajax.serializeForm(form);
11623     }
11624 });/*
11625  * Based on:
11626  * Ext JS Library 1.1.1
11627  * Copyright(c) 2006-2007, Ext JS, LLC.
11628  *
11629  * Originally Released Under LGPL - original licence link has changed is not relivant.
11630  *
11631  * Fork - LGPL
11632  * <script type="text/javascript">
11633  */
11634  
11635 /**
11636  * Global Ajax request class.
11637  * 
11638  * @class Roo.Ajax
11639  * @extends Roo.data.Connection
11640  * @static
11641  * 
11642  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11643  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11644  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11645  * @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)
11646  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11647  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11648  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11649  */
11650 Roo.Ajax = new Roo.data.Connection({
11651     // fix up the docs
11652     /**
11653      * @scope Roo.Ajax
11654      * @type {Boolear} 
11655      */
11656     autoAbort : false,
11657
11658     /**
11659      * Serialize the passed form into a url encoded string
11660      * @scope Roo.Ajax
11661      * @param {String/HTMLElement} form
11662      * @return {String}
11663      */
11664     serializeForm : function(form){
11665         return Roo.lib.Ajax.serializeForm(form);
11666     }
11667 });/*
11668  * Based on:
11669  * Ext JS Library 1.1.1
11670  * Copyright(c) 2006-2007, Ext JS, LLC.
11671  *
11672  * Originally Released Under LGPL - original licence link has changed is not relivant.
11673  *
11674  * Fork - LGPL
11675  * <script type="text/javascript">
11676  */
11677
11678  
11679 /**
11680  * @class Roo.UpdateManager
11681  * @extends Roo.util.Observable
11682  * Provides AJAX-style update for Element object.<br><br>
11683  * Usage:<br>
11684  * <pre><code>
11685  * // Get it from a Roo.Element object
11686  * var el = Roo.get("foo");
11687  * var mgr = el.getUpdateManager();
11688  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11689  * ...
11690  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11691  * <br>
11692  * // or directly (returns the same UpdateManager instance)
11693  * var mgr = new Roo.UpdateManager("myElementId");
11694  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11695  * mgr.on("update", myFcnNeedsToKnow);
11696  * <br>
11697    // short handed call directly from the element object
11698    Roo.get("foo").load({
11699         url: "bar.php",
11700         scripts:true,
11701         params: "for=bar",
11702         text: "Loading Foo..."
11703    });
11704  * </code></pre>
11705  * @constructor
11706  * Create new UpdateManager directly.
11707  * @param {String/HTMLElement/Roo.Element} el The element to update
11708  * @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).
11709  */
11710 Roo.UpdateManager = function(el, forceNew){
11711     el = Roo.get(el);
11712     if(!forceNew && el.updateManager){
11713         return el.updateManager;
11714     }
11715     /**
11716      * The Element object
11717      * @type Roo.Element
11718      */
11719     this.el = el;
11720     /**
11721      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11722      * @type String
11723      */
11724     this.defaultUrl = null;
11725
11726     this.addEvents({
11727         /**
11728          * @event beforeupdate
11729          * Fired before an update is made, return false from your handler and the update is cancelled.
11730          * @param {Roo.Element} el
11731          * @param {String/Object/Function} url
11732          * @param {String/Object} params
11733          */
11734         "beforeupdate": true,
11735         /**
11736          * @event update
11737          * Fired after successful update is made.
11738          * @param {Roo.Element} el
11739          * @param {Object} oResponseObject The response Object
11740          */
11741         "update": true,
11742         /**
11743          * @event failure
11744          * Fired on update failure.
11745          * @param {Roo.Element} el
11746          * @param {Object} oResponseObject The response Object
11747          */
11748         "failure": true
11749     });
11750     var d = Roo.UpdateManager.defaults;
11751     /**
11752      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11753      * @type String
11754      */
11755     this.sslBlankUrl = d.sslBlankUrl;
11756     /**
11757      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11758      * @type Boolean
11759      */
11760     this.disableCaching = d.disableCaching;
11761     /**
11762      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11763      * @type String
11764      */
11765     this.indicatorText = d.indicatorText;
11766     /**
11767      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11768      * @type String
11769      */
11770     this.showLoadIndicator = d.showLoadIndicator;
11771     /**
11772      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11773      * @type Number
11774      */
11775     this.timeout = d.timeout;
11776
11777     /**
11778      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11779      * @type Boolean
11780      */
11781     this.loadScripts = d.loadScripts;
11782
11783     /**
11784      * Transaction object of current executing transaction
11785      */
11786     this.transaction = null;
11787
11788     /**
11789      * @private
11790      */
11791     this.autoRefreshProcId = null;
11792     /**
11793      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11794      * @type Function
11795      */
11796     this.refreshDelegate = this.refresh.createDelegate(this);
11797     /**
11798      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11799      * @type Function
11800      */
11801     this.updateDelegate = this.update.createDelegate(this);
11802     /**
11803      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11804      * @type Function
11805      */
11806     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11807     /**
11808      * @private
11809      */
11810     this.successDelegate = this.processSuccess.createDelegate(this);
11811     /**
11812      * @private
11813      */
11814     this.failureDelegate = this.processFailure.createDelegate(this);
11815
11816     if(!this.renderer){
11817      /**
11818       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11819       */
11820     this.renderer = new Roo.UpdateManager.BasicRenderer();
11821     }
11822     
11823     Roo.UpdateManager.superclass.constructor.call(this);
11824 };
11825
11826 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11827     /**
11828      * Get the Element this UpdateManager is bound to
11829      * @return {Roo.Element} The element
11830      */
11831     getEl : function(){
11832         return this.el;
11833     },
11834     /**
11835      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11836      * @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:
11837 <pre><code>
11838 um.update({<br/>
11839     url: "your-url.php",<br/>
11840     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11841     callback: yourFunction,<br/>
11842     scope: yourObject, //(optional scope)  <br/>
11843     discardUrl: false, <br/>
11844     nocache: false,<br/>
11845     text: "Loading...",<br/>
11846     timeout: 30,<br/>
11847     scripts: false<br/>
11848 });
11849 </code></pre>
11850      * The only required property is url. The optional properties nocache, text and scripts
11851      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11852      * @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}
11853      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11854      * @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.
11855      */
11856     update : function(url, params, callback, discardUrl){
11857         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11858             var method = this.method, cfg;
11859             if(typeof url == "object"){ // must be config object
11860                 cfg = url;
11861                 url = cfg.url;
11862                 params = params || cfg.params;
11863                 callback = callback || cfg.callback;
11864                 discardUrl = discardUrl || cfg.discardUrl;
11865                 if(callback && cfg.scope){
11866                     callback = callback.createDelegate(cfg.scope);
11867                 }
11868                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11869                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11870                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11871                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11872                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11873             }
11874             this.showLoading();
11875             if(!discardUrl){
11876                 this.defaultUrl = url;
11877             }
11878             if(typeof url == "function"){
11879                 url = url.call(this);
11880             }
11881
11882             method = method || (params ? "POST" : "GET");
11883             if(method == "GET"){
11884                 url = this.prepareUrl(url);
11885             }
11886
11887             var o = Roo.apply(cfg ||{}, {
11888                 url : url,
11889                 params: params,
11890                 success: this.successDelegate,
11891                 failure: this.failureDelegate,
11892                 callback: undefined,
11893                 timeout: (this.timeout*1000),
11894                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11895             });
11896
11897             this.transaction = Roo.Ajax.request(o);
11898         }
11899     },
11900
11901     /**
11902      * 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.
11903      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11904      * @param {String/HTMLElement} form The form Id or form element
11905      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11906      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11907      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11908      */
11909     formUpdate : function(form, url, reset, callback){
11910         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11911             if(typeof url == "function"){
11912                 url = url.call(this);
11913             }
11914             form = Roo.getDom(form);
11915             this.transaction = Roo.Ajax.request({
11916                 form: form,
11917                 url:url,
11918                 success: this.successDelegate,
11919                 failure: this.failureDelegate,
11920                 timeout: (this.timeout*1000),
11921                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11922             });
11923             this.showLoading.defer(1, this);
11924         }
11925     },
11926
11927     /**
11928      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11929      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11930      */
11931     refresh : function(callback){
11932         if(this.defaultUrl == null){
11933             return;
11934         }
11935         this.update(this.defaultUrl, null, callback, true);
11936     },
11937
11938     /**
11939      * Set this element to auto refresh.
11940      * @param {Number} interval How often to update (in seconds).
11941      * @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)
11942      * @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}
11943      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11944      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11945      */
11946     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11947         if(refreshNow){
11948             this.update(url || this.defaultUrl, params, callback, true);
11949         }
11950         if(this.autoRefreshProcId){
11951             clearInterval(this.autoRefreshProcId);
11952         }
11953         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11954     },
11955
11956     /**
11957      * Stop auto refresh on this element.
11958      */
11959      stopAutoRefresh : function(){
11960         if(this.autoRefreshProcId){
11961             clearInterval(this.autoRefreshProcId);
11962             delete this.autoRefreshProcId;
11963         }
11964     },
11965
11966     isAutoRefreshing : function(){
11967        return this.autoRefreshProcId ? true : false;
11968     },
11969     /**
11970      * Called to update the element to "Loading" state. Override to perform custom action.
11971      */
11972     showLoading : function(){
11973         if(this.showLoadIndicator){
11974             this.el.update(this.indicatorText);
11975         }
11976     },
11977
11978     /**
11979      * Adds unique parameter to query string if disableCaching = true
11980      * @private
11981      */
11982     prepareUrl : function(url){
11983         if(this.disableCaching){
11984             var append = "_dc=" + (new Date().getTime());
11985             if(url.indexOf("?") !== -1){
11986                 url += "&" + append;
11987             }else{
11988                 url += "?" + append;
11989             }
11990         }
11991         return url;
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processSuccess : function(response){
11998         this.transaction = null;
11999         if(response.argument.form && response.argument.reset){
12000             try{ // put in try/catch since some older FF releases had problems with this
12001                 response.argument.form.reset();
12002             }catch(e){}
12003         }
12004         if(this.loadScripts){
12005             this.renderer.render(this.el, response, this,
12006                 this.updateComplete.createDelegate(this, [response]));
12007         }else{
12008             this.renderer.render(this.el, response, this);
12009             this.updateComplete(response);
12010         }
12011     },
12012
12013     updateComplete : function(response){
12014         this.fireEvent("update", this.el, response);
12015         if(typeof response.argument.callback == "function"){
12016             response.argument.callback(this.el, true, response);
12017         }
12018     },
12019
12020     /**
12021      * @private
12022      */
12023     processFailure : function(response){
12024         this.transaction = null;
12025         this.fireEvent("failure", this.el, response);
12026         if(typeof response.argument.callback == "function"){
12027             response.argument.callback(this.el, false, response);
12028         }
12029     },
12030
12031     /**
12032      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12033      * @param {Object} renderer The object implementing the render() method
12034      */
12035     setRenderer : function(renderer){
12036         this.renderer = renderer;
12037     },
12038
12039     getRenderer : function(){
12040        return this.renderer;
12041     },
12042
12043     /**
12044      * Set the defaultUrl used for updates
12045      * @param {String/Function} defaultUrl The url or a function to call to get the url
12046      */
12047     setDefaultUrl : function(defaultUrl){
12048         this.defaultUrl = defaultUrl;
12049     },
12050
12051     /**
12052      * Aborts the executing transaction
12053      */
12054     abort : function(){
12055         if(this.transaction){
12056             Roo.Ajax.abort(this.transaction);
12057         }
12058     },
12059
12060     /**
12061      * Returns true if an update is in progress
12062      * @return {Boolean}
12063      */
12064     isUpdating : function(){
12065         if(this.transaction){
12066             return Roo.Ajax.isLoading(this.transaction);
12067         }
12068         return false;
12069     }
12070 });
12071
12072 /**
12073  * @class Roo.UpdateManager.defaults
12074  * @static (not really - but it helps the doc tool)
12075  * The defaults collection enables customizing the default properties of UpdateManager
12076  */
12077    Roo.UpdateManager.defaults = {
12078        /**
12079          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12080          * @type Number
12081          */
12082          timeout : 30,
12083
12084          /**
12085          * True to process scripts by default (Defaults to false).
12086          * @type Boolean
12087          */
12088         loadScripts : false,
12089
12090         /**
12091         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12092         * @type String
12093         */
12094         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12095         /**
12096          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12097          * @type Boolean
12098          */
12099         disableCaching : false,
12100         /**
12101          * Whether to show indicatorText when loading (Defaults to true).
12102          * @type Boolean
12103          */
12104         showLoadIndicator : true,
12105         /**
12106          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12107          * @type String
12108          */
12109         indicatorText : '<div class="loading-indicator">Loading...</div>'
12110    };
12111
12112 /**
12113  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12114  *Usage:
12115  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12116  * @param {String/HTMLElement/Roo.Element} el The element to update
12117  * @param {String} url The url
12118  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12119  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12120  * @static
12121  * @deprecated
12122  * @member Roo.UpdateManager
12123  */
12124 Roo.UpdateManager.updateElement = function(el, url, params, options){
12125     var um = Roo.get(el, true).getUpdateManager();
12126     Roo.apply(um, options);
12127     um.update(url, params, options ? options.callback : null);
12128 };
12129 // alias for backwards compat
12130 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12131 /**
12132  * @class Roo.UpdateManager.BasicRenderer
12133  * Default Content renderer. Updates the elements innerHTML with the responseText.
12134  */
12135 Roo.UpdateManager.BasicRenderer = function(){};
12136
12137 Roo.UpdateManager.BasicRenderer.prototype = {
12138     /**
12139      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12140      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12141      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12142      * @param {Roo.Element} el The element being rendered
12143      * @param {Object} response The YUI Connect response object
12144      * @param {UpdateManager} updateManager The calling update manager
12145      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12146      */
12147      render : function(el, response, updateManager, callback){
12148         el.update(response.responseText, updateManager.loadScripts, callback);
12149     }
12150 };
12151 /*
12152  * Based on:
12153  * Ext JS Library 1.1.1
12154  * Copyright(c) 2006-2007, Ext JS, LLC.
12155  *
12156  * Originally Released Under LGPL - original licence link has changed is not relivant.
12157  *
12158  * Fork - LGPL
12159  * <script type="text/javascript">
12160  */
12161
12162 /**
12163  * @class Roo.util.DelayedTask
12164  * Provides a convenient method of performing setTimeout where a new
12165  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12166  * You can use this class to buffer
12167  * the keypress events for a certain number of milliseconds, and perform only if they stop
12168  * for that amount of time.
12169  * @constructor The parameters to this constructor serve as defaults and are not required.
12170  * @param {Function} fn (optional) The default function to timeout
12171  * @param {Object} scope (optional) The default scope of that timeout
12172  * @param {Array} args (optional) The default Array of arguments
12173  */
12174 Roo.util.DelayedTask = function(fn, scope, args){
12175     var id = null, d, t;
12176
12177     var call = function(){
12178         var now = new Date().getTime();
12179         if(now - t >= d){
12180             clearInterval(id);
12181             id = null;
12182             fn.apply(scope, args || []);
12183         }
12184     };
12185     /**
12186      * Cancels any pending timeout and queues a new one
12187      * @param {Number} delay The milliseconds to delay
12188      * @param {Function} newFn (optional) Overrides function passed to constructor
12189      * @param {Object} newScope (optional) Overrides scope passed to constructor
12190      * @param {Array} newArgs (optional) Overrides args passed to constructor
12191      */
12192     this.delay = function(delay, newFn, newScope, newArgs){
12193         if(id && delay != d){
12194             this.cancel();
12195         }
12196         d = delay;
12197         t = new Date().getTime();
12198         fn = newFn || fn;
12199         scope = newScope || scope;
12200         args = newArgs || args;
12201         if(!id){
12202             id = setInterval(call, d);
12203         }
12204     };
12205
12206     /**
12207      * Cancel the last queued timeout
12208      */
12209     this.cancel = function(){
12210         if(id){
12211             clearInterval(id);
12212             id = null;
12213         }
12214     };
12215 };/*
12216  * Based on:
12217  * Ext JS Library 1.1.1
12218  * Copyright(c) 2006-2007, Ext JS, LLC.
12219  *
12220  * Originally Released Under LGPL - original licence link has changed is not relivant.
12221  *
12222  * Fork - LGPL
12223  * <script type="text/javascript">
12224  */
12225  
12226  
12227 Roo.util.TaskRunner = function(interval){
12228     interval = interval || 10;
12229     var tasks = [], removeQueue = [];
12230     var id = 0;
12231     var running = false;
12232
12233     var stopThread = function(){
12234         running = false;
12235         clearInterval(id);
12236         id = 0;
12237     };
12238
12239     var startThread = function(){
12240         if(!running){
12241             running = true;
12242             id = setInterval(runTasks, interval);
12243         }
12244     };
12245
12246     var removeTask = function(task){
12247         removeQueue.push(task);
12248         if(task.onStop){
12249             task.onStop();
12250         }
12251     };
12252
12253     var runTasks = function(){
12254         if(removeQueue.length > 0){
12255             for(var i = 0, len = removeQueue.length; i < len; i++){
12256                 tasks.remove(removeQueue[i]);
12257             }
12258             removeQueue = [];
12259             if(tasks.length < 1){
12260                 stopThread();
12261                 return;
12262             }
12263         }
12264         var now = new Date().getTime();
12265         for(var i = 0, len = tasks.length; i < len; ++i){
12266             var t = tasks[i];
12267             var itime = now - t.taskRunTime;
12268             if(t.interval <= itime){
12269                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12270                 t.taskRunTime = now;
12271                 if(rt === false || t.taskRunCount === t.repeat){
12272                     removeTask(t);
12273                     return;
12274                 }
12275             }
12276             if(t.duration && t.duration <= (now - t.taskStartTime)){
12277                 removeTask(t);
12278             }
12279         }
12280     };
12281
12282     /**
12283      * Queues a new task.
12284      * @param {Object} task
12285      */
12286     this.start = function(task){
12287         tasks.push(task);
12288         task.taskStartTime = new Date().getTime();
12289         task.taskRunTime = 0;
12290         task.taskRunCount = 0;
12291         startThread();
12292         return task;
12293     };
12294
12295     this.stop = function(task){
12296         removeTask(task);
12297         return task;
12298     };
12299
12300     this.stopAll = function(){
12301         stopThread();
12302         for(var i = 0, len = tasks.length; i < len; i++){
12303             if(tasks[i].onStop){
12304                 tasks[i].onStop();
12305             }
12306         }
12307         tasks = [];
12308         removeQueue = [];
12309     };
12310 };
12311
12312 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12313  * Based on:
12314  * Ext JS Library 1.1.1
12315  * Copyright(c) 2006-2007, Ext JS, LLC.
12316  *
12317  * Originally Released Under LGPL - original licence link has changed is not relivant.
12318  *
12319  * Fork - LGPL
12320  * <script type="text/javascript">
12321  */
12322
12323  
12324 /**
12325  * @class Roo.util.MixedCollection
12326  * @extends Roo.util.Observable
12327  * A Collection class that maintains both numeric indexes and keys and exposes events.
12328  * @constructor
12329  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12330  * collection (defaults to false)
12331  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12332  * and return the key value for that item.  This is used when available to look up the key on items that
12333  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12334  * equivalent to providing an implementation for the {@link #getKey} method.
12335  */
12336 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12337     this.items = [];
12338     this.map = {};
12339     this.keys = [];
12340     this.length = 0;
12341     this.addEvents({
12342         /**
12343          * @event clear
12344          * Fires when the collection is cleared.
12345          */
12346         "clear" : true,
12347         /**
12348          * @event add
12349          * Fires when an item is added to the collection.
12350          * @param {Number} index The index at which the item was added.
12351          * @param {Object} o The item added.
12352          * @param {String} key The key associated with the added item.
12353          */
12354         "add" : true,
12355         /**
12356          * @event replace
12357          * Fires when an item is replaced in the collection.
12358          * @param {String} key he key associated with the new added.
12359          * @param {Object} old The item being replaced.
12360          * @param {Object} new The new item.
12361          */
12362         "replace" : true,
12363         /**
12364          * @event remove
12365          * Fires when an item is removed from the collection.
12366          * @param {Object} o The item being removed.
12367          * @param {String} key (optional) The key associated with the removed item.
12368          */
12369         "remove" : true,
12370         "sort" : true
12371     });
12372     this.allowFunctions = allowFunctions === true;
12373     if(keyFn){
12374         this.getKey = keyFn;
12375     }
12376     Roo.util.MixedCollection.superclass.constructor.call(this);
12377 };
12378
12379 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12380     allowFunctions : false,
12381     
12382 /**
12383  * Adds an item to the collection.
12384  * @param {String} key The key to associate with the item
12385  * @param {Object} o The item to add.
12386  * @return {Object} The item added.
12387  */
12388     add : function(key, o){
12389         if(arguments.length == 1){
12390             o = arguments[0];
12391             key = this.getKey(o);
12392         }
12393         if(typeof key == "undefined" || key === null){
12394             this.length++;
12395             this.items.push(o);
12396             this.keys.push(null);
12397         }else{
12398             var old = this.map[key];
12399             if(old){
12400                 return this.replace(key, o);
12401             }
12402             this.length++;
12403             this.items.push(o);
12404             this.map[key] = o;
12405             this.keys.push(key);
12406         }
12407         this.fireEvent("add", this.length-1, o, key);
12408         return o;
12409     },
12410        
12411 /**
12412   * MixedCollection has a generic way to fetch keys if you implement getKey.
12413 <pre><code>
12414 // normal way
12415 var mc = new Roo.util.MixedCollection();
12416 mc.add(someEl.dom.id, someEl);
12417 mc.add(otherEl.dom.id, otherEl);
12418 //and so on
12419
12420 // using getKey
12421 var mc = new Roo.util.MixedCollection();
12422 mc.getKey = function(el){
12423    return el.dom.id;
12424 };
12425 mc.add(someEl);
12426 mc.add(otherEl);
12427
12428 // or via the constructor
12429 var mc = new Roo.util.MixedCollection(false, function(el){
12430    return el.dom.id;
12431 });
12432 mc.add(someEl);
12433 mc.add(otherEl);
12434 </code></pre>
12435  * @param o {Object} The item for which to find the key.
12436  * @return {Object} The key for the passed item.
12437  */
12438     getKey : function(o){
12439          return o.id; 
12440     },
12441    
12442 /**
12443  * Replaces an item in the collection.
12444  * @param {String} key The key associated with the item to replace, or the item to replace.
12445  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12446  * @return {Object}  The new item.
12447  */
12448     replace : function(key, o){
12449         if(arguments.length == 1){
12450             o = arguments[0];
12451             key = this.getKey(o);
12452         }
12453         var old = this.item(key);
12454         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12455              return this.add(key, o);
12456         }
12457         var index = this.indexOfKey(key);
12458         this.items[index] = o;
12459         this.map[key] = o;
12460         this.fireEvent("replace", key, old, o);
12461         return o;
12462     },
12463    
12464 /**
12465  * Adds all elements of an Array or an Object to the collection.
12466  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12467  * an Array of values, each of which are added to the collection.
12468  */
12469     addAll : function(objs){
12470         if(arguments.length > 1 || objs instanceof Array){
12471             var args = arguments.length > 1 ? arguments : objs;
12472             for(var i = 0, len = args.length; i < len; i++){
12473                 this.add(args[i]);
12474             }
12475         }else{
12476             for(var key in objs){
12477                 if(this.allowFunctions || typeof objs[key] != "function"){
12478                     this.add(key, objs[key]);
12479                 }
12480             }
12481         }
12482     },
12483    
12484 /**
12485  * Executes the specified function once for every item in the collection, passing each
12486  * item as the first and only parameter. returning false from the function will stop the iteration.
12487  * @param {Function} fn The function to execute for each item.
12488  * @param {Object} scope (optional) The scope in which to execute the function.
12489  */
12490     each : function(fn, scope){
12491         var items = [].concat(this.items); // each safe for removal
12492         for(var i = 0, len = items.length; i < len; i++){
12493             if(fn.call(scope || items[i], items[i], i, len) === false){
12494                 break;
12495             }
12496         }
12497     },
12498    
12499 /**
12500  * Executes the specified function once for every key in the collection, passing each
12501  * key, and its associated item as the first two parameters.
12502  * @param {Function} fn The function to execute for each item.
12503  * @param {Object} scope (optional) The scope in which to execute the function.
12504  */
12505     eachKey : function(fn, scope){
12506         for(var i = 0, len = this.keys.length; i < len; i++){
12507             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12508         }
12509     },
12510    
12511 /**
12512  * Returns the first item in the collection which elicits a true return value from the
12513  * passed selection function.
12514  * @param {Function} fn The selection function to execute for each item.
12515  * @param {Object} scope (optional) The scope in which to execute the function.
12516  * @return {Object} The first item in the collection which returned true from the selection function.
12517  */
12518     find : function(fn, scope){
12519         for(var i = 0, len = this.items.length; i < len; i++){
12520             if(fn.call(scope || window, this.items[i], this.keys[i])){
12521                 return this.items[i];
12522             }
12523         }
12524         return null;
12525     },
12526    
12527 /**
12528  * Inserts an item at the specified index in the collection.
12529  * @param {Number} index The index to insert the item at.
12530  * @param {String} key The key to associate with the new item, or the item itself.
12531  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12532  * @return {Object} The item inserted.
12533  */
12534     insert : function(index, key, o){
12535         if(arguments.length == 2){
12536             o = arguments[1];
12537             key = this.getKey(o);
12538         }
12539         if(index >= this.length){
12540             return this.add(key, o);
12541         }
12542         this.length++;
12543         this.items.splice(index, 0, o);
12544         if(typeof key != "undefined" && key != null){
12545             this.map[key] = o;
12546         }
12547         this.keys.splice(index, 0, key);
12548         this.fireEvent("add", index, o, key);
12549         return o;
12550     },
12551    
12552 /**
12553  * Removed an item from the collection.
12554  * @param {Object} o The item to remove.
12555  * @return {Object} The item removed.
12556  */
12557     remove : function(o){
12558         return this.removeAt(this.indexOf(o));
12559     },
12560    
12561 /**
12562  * Remove an item from a specified index in the collection.
12563  * @param {Number} index The index within the collection of the item to remove.
12564  */
12565     removeAt : function(index){
12566         if(index < this.length && index >= 0){
12567             this.length--;
12568             var o = this.items[index];
12569             this.items.splice(index, 1);
12570             var key = this.keys[index];
12571             if(typeof key != "undefined"){
12572                 delete this.map[key];
12573             }
12574             this.keys.splice(index, 1);
12575             this.fireEvent("remove", o, key);
12576         }
12577     },
12578    
12579 /**
12580  * Removed an item associated with the passed key fom the collection.
12581  * @param {String} key The key of the item to remove.
12582  */
12583     removeKey : function(key){
12584         return this.removeAt(this.indexOfKey(key));
12585     },
12586    
12587 /**
12588  * Returns the number of items in the collection.
12589  * @return {Number} the number of items in the collection.
12590  */
12591     getCount : function(){
12592         return this.length; 
12593     },
12594    
12595 /**
12596  * Returns index within the collection of the passed Object.
12597  * @param {Object} o The item to find the index of.
12598  * @return {Number} index of the item.
12599  */
12600     indexOf : function(o){
12601         if(!this.items.indexOf){
12602             for(var i = 0, len = this.items.length; i < len; i++){
12603                 if(this.items[i] == o) return i;
12604             }
12605             return -1;
12606         }else{
12607             return this.items.indexOf(o);
12608         }
12609     },
12610    
12611 /**
12612  * Returns index within the collection of the passed key.
12613  * @param {String} key The key to find the index of.
12614  * @return {Number} index of the key.
12615  */
12616     indexOfKey : function(key){
12617         if(!this.keys.indexOf){
12618             for(var i = 0, len = this.keys.length; i < len; i++){
12619                 if(this.keys[i] == key) return i;
12620             }
12621             return -1;
12622         }else{
12623             return this.keys.indexOf(key);
12624         }
12625     },
12626    
12627 /**
12628  * Returns the item associated with the passed key OR index. Key has priority over index.
12629  * @param {String/Number} key The key or index of the item.
12630  * @return {Object} The item associated with the passed key.
12631  */
12632     item : function(key){
12633         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12634         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12635     },
12636     
12637 /**
12638  * Returns the item at the specified index.
12639  * @param {Number} index The index of the item.
12640  * @return {Object}
12641  */
12642     itemAt : function(index){
12643         return this.items[index];
12644     },
12645     
12646 /**
12647  * Returns the item associated with the passed key.
12648  * @param {String/Number} key The key of the item.
12649  * @return {Object} The item associated with the passed key.
12650  */
12651     key : function(key){
12652         return this.map[key];
12653     },
12654    
12655 /**
12656  * Returns true if the collection contains the passed Object as an item.
12657  * @param {Object} o  The Object to look for in the collection.
12658  * @return {Boolean} True if the collection contains the Object as an item.
12659  */
12660     contains : function(o){
12661         return this.indexOf(o) != -1;
12662     },
12663    
12664 /**
12665  * Returns true if the collection contains the passed Object as a key.
12666  * @param {String} key The key to look for in the collection.
12667  * @return {Boolean} True if the collection contains the Object as a key.
12668  */
12669     containsKey : function(key){
12670         return typeof this.map[key] != "undefined";
12671     },
12672    
12673 /**
12674  * Removes all items from the collection.
12675  */
12676     clear : function(){
12677         this.length = 0;
12678         this.items = [];
12679         this.keys = [];
12680         this.map = {};
12681         this.fireEvent("clear");
12682     },
12683    
12684 /**
12685  * Returns the first item in the collection.
12686  * @return {Object} the first item in the collection..
12687  */
12688     first : function(){
12689         return this.items[0]; 
12690     },
12691    
12692 /**
12693  * Returns the last item in the collection.
12694  * @return {Object} the last item in the collection..
12695  */
12696     last : function(){
12697         return this.items[this.length-1];   
12698     },
12699     
12700     _sort : function(property, dir, fn){
12701         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12702         fn = fn || function(a, b){
12703             return a-b;
12704         };
12705         var c = [], k = this.keys, items = this.items;
12706         for(var i = 0, len = items.length; i < len; i++){
12707             c[c.length] = {key: k[i], value: items[i], index: i};
12708         }
12709         c.sort(function(a, b){
12710             var v = fn(a[property], b[property]) * dsc;
12711             if(v == 0){
12712                 v = (a.index < b.index ? -1 : 1);
12713             }
12714             return v;
12715         });
12716         for(var i = 0, len = c.length; i < len; i++){
12717             items[i] = c[i].value;
12718             k[i] = c[i].key;
12719         }
12720         this.fireEvent("sort", this);
12721     },
12722     
12723     /**
12724      * Sorts this collection with the passed comparison function
12725      * @param {String} direction (optional) "ASC" or "DESC"
12726      * @param {Function} fn (optional) comparison function
12727      */
12728     sort : function(dir, fn){
12729         this._sort("value", dir, fn);
12730     },
12731     
12732     /**
12733      * Sorts this collection by keys
12734      * @param {String} direction (optional) "ASC" or "DESC"
12735      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12736      */
12737     keySort : function(dir, fn){
12738         this._sort("key", dir, fn || function(a, b){
12739             return String(a).toUpperCase()-String(b).toUpperCase();
12740         });
12741     },
12742     
12743     /**
12744      * Returns a range of items in this collection
12745      * @param {Number} startIndex (optional) defaults to 0
12746      * @param {Number} endIndex (optional) default to the last item
12747      * @return {Array} An array of items
12748      */
12749     getRange : function(start, end){
12750         var items = this.items;
12751         if(items.length < 1){
12752             return [];
12753         }
12754         start = start || 0;
12755         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12756         var r = [];
12757         if(start <= end){
12758             for(var i = start; i <= end; i++) {
12759                     r[r.length] = items[i];
12760             }
12761         }else{
12762             for(var i = start; i >= end; i--) {
12763                     r[r.length] = items[i];
12764             }
12765         }
12766         return r;
12767     },
12768         
12769     /**
12770      * Filter the <i>objects</i> in this collection by a specific property. 
12771      * Returns a new collection that has been filtered.
12772      * @param {String} property A property on your objects
12773      * @param {String/RegExp} value Either string that the property values 
12774      * should start with or a RegExp to test against the property
12775      * @return {MixedCollection} The new filtered collection
12776      */
12777     filter : function(property, value){
12778         if(!value.exec){ // not a regex
12779             value = String(value);
12780             if(value.length == 0){
12781                 return this.clone();
12782             }
12783             value = new RegExp("^" + Roo.escapeRe(value), "i");
12784         }
12785         return this.filterBy(function(o){
12786             return o && value.test(o[property]);
12787         });
12788         },
12789     
12790     /**
12791      * Filter by a function. * Returns a new collection that has been filtered.
12792      * The passed function will be called with each 
12793      * object in the collection. If the function returns true, the value is included 
12794      * otherwise it is filtered.
12795      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12796      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12797      * @return {MixedCollection} The new filtered collection
12798      */
12799     filterBy : function(fn, scope){
12800         var r = new Roo.util.MixedCollection();
12801         r.getKey = this.getKey;
12802         var k = this.keys, it = this.items;
12803         for(var i = 0, len = it.length; i < len; i++){
12804             if(fn.call(scope||this, it[i], k[i])){
12805                                 r.add(k[i], it[i]);
12806                         }
12807         }
12808         return r;
12809     },
12810     
12811     /**
12812      * Creates a duplicate of this collection
12813      * @return {MixedCollection}
12814      */
12815     clone : function(){
12816         var r = new Roo.util.MixedCollection();
12817         var k = this.keys, it = this.items;
12818         for(var i = 0, len = it.length; i < len; i++){
12819             r.add(k[i], it[i]);
12820         }
12821         r.getKey = this.getKey;
12822         return r;
12823     }
12824 });
12825 /**
12826  * Returns the item associated with the passed key or index.
12827  * @method
12828  * @param {String/Number} key The key or index of the item.
12829  * @return {Object} The item associated with the passed key.
12830  */
12831 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12832  * Based on:
12833  * Ext JS Library 1.1.1
12834  * Copyright(c) 2006-2007, Ext JS, LLC.
12835  *
12836  * Originally Released Under LGPL - original licence link has changed is not relivant.
12837  *
12838  * Fork - LGPL
12839  * <script type="text/javascript">
12840  */
12841 /**
12842  * @class Roo.util.JSON
12843  * Modified version of Douglas Crockford"s json.js that doesn"t
12844  * mess with the Object prototype 
12845  * http://www.json.org/js.html
12846  * @singleton
12847  */
12848 Roo.util.JSON = new (function(){
12849     var useHasOwn = {}.hasOwnProperty ? true : false;
12850     
12851     // crashes Safari in some instances
12852     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12853     
12854     var pad = function(n) {
12855         return n < 10 ? "0" + n : n;
12856     };
12857     
12858     var m = {
12859         "\b": '\\b',
12860         "\t": '\\t',
12861         "\n": '\\n',
12862         "\f": '\\f',
12863         "\r": '\\r',
12864         '"' : '\\"',
12865         "\\": '\\\\'
12866     };
12867
12868     var encodeString = function(s){
12869         if (/["\\\x00-\x1f]/.test(s)) {
12870             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12871                 var c = m[b];
12872                 if(c){
12873                     return c;
12874                 }
12875                 c = b.charCodeAt();
12876                 return "\\u00" +
12877                     Math.floor(c / 16).toString(16) +
12878                     (c % 16).toString(16);
12879             }) + '"';
12880         }
12881         return '"' + s + '"';
12882     };
12883     
12884     var encodeArray = function(o){
12885         var a = ["["], b, i, l = o.length, v;
12886             for (i = 0; i < l; i += 1) {
12887                 v = o[i];
12888                 switch (typeof v) {
12889                     case "undefined":
12890                     case "function":
12891                     case "unknown":
12892                         break;
12893                     default:
12894                         if (b) {
12895                             a.push(',');
12896                         }
12897                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12898                         b = true;
12899                 }
12900             }
12901             a.push("]");
12902             return a.join("");
12903     };
12904     
12905     var encodeDate = function(o){
12906         return '"' + o.getFullYear() + "-" +
12907                 pad(o.getMonth() + 1) + "-" +
12908                 pad(o.getDate()) + "T" +
12909                 pad(o.getHours()) + ":" +
12910                 pad(o.getMinutes()) + ":" +
12911                 pad(o.getSeconds()) + '"';
12912     };
12913     
12914     /**
12915      * Encodes an Object, Array or other value
12916      * @param {Mixed} o The variable to encode
12917      * @return {String} The JSON string
12918      */
12919     this.encode = function(o)
12920     {
12921         // should this be extended to fully wrap stringify..
12922         
12923         if(typeof o == "undefined" || o === null){
12924             return "null";
12925         }else if(o instanceof Array){
12926             return encodeArray(o);
12927         }else if(o instanceof Date){
12928             return encodeDate(o);
12929         }else if(typeof o == "string"){
12930             return encodeString(o);
12931         }else if(typeof o == "number"){
12932             return isFinite(o) ? String(o) : "null";
12933         }else if(typeof o == "boolean"){
12934             return String(o);
12935         }else {
12936             var a = ["{"], b, i, v;
12937             for (i in o) {
12938                 if(!useHasOwn || o.hasOwnProperty(i)) {
12939                     v = o[i];
12940                     switch (typeof v) {
12941                     case "undefined":
12942                     case "function":
12943                     case "unknown":
12944                         break;
12945                     default:
12946                         if(b){
12947                             a.push(',');
12948                         }
12949                         a.push(this.encode(i), ":",
12950                                 v === null ? "null" : this.encode(v));
12951                         b = true;
12952                     }
12953                 }
12954             }
12955             a.push("}");
12956             return a.join("");
12957         }
12958     };
12959     
12960     /**
12961      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12962      * @param {String} json The JSON string
12963      * @return {Object} The resulting object
12964      */
12965     this.decode = function(json){
12966         
12967         return  /** eval:var:json */ eval("(" + json + ')');
12968     };
12969 })();
12970 /** 
12971  * Shorthand for {@link Roo.util.JSON#encode}
12972  * @member Roo encode 
12973  * @method */
12974 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12975 /** 
12976  * Shorthand for {@link Roo.util.JSON#decode}
12977  * @member Roo decode 
12978  * @method */
12979 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12980 /*
12981  * Based on:
12982  * Ext JS Library 1.1.1
12983  * Copyright(c) 2006-2007, Ext JS, LLC.
12984  *
12985  * Originally Released Under LGPL - original licence link has changed is not relivant.
12986  *
12987  * Fork - LGPL
12988  * <script type="text/javascript">
12989  */
12990  
12991 /**
12992  * @class Roo.util.Format
12993  * Reusable data formatting functions
12994  * @singleton
12995  */
12996 Roo.util.Format = function(){
12997     var trimRe = /^\s+|\s+$/g;
12998     return {
12999         /**
13000          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13001          * @param {String} value The string to truncate
13002          * @param {Number} length The maximum length to allow before truncating
13003          * @return {String} The converted text
13004          */
13005         ellipsis : function(value, len){
13006             if(value && value.length > len){
13007                 return value.substr(0, len-3)+"...";
13008             }
13009             return value;
13010         },
13011
13012         /**
13013          * Checks a reference and converts it to empty string if it is undefined
13014          * @param {Mixed} value Reference to check
13015          * @return {Mixed} Empty string if converted, otherwise the original value
13016          */
13017         undef : function(value){
13018             return typeof value != "undefined" ? value : "";
13019         },
13020
13021         /**
13022          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13023          * @param {String} value The string to encode
13024          * @return {String} The encoded text
13025          */
13026         htmlEncode : function(value){
13027             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13028         },
13029
13030         /**
13031          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13032          * @param {String} value The string to decode
13033          * @return {String} The decoded text
13034          */
13035         htmlDecode : function(value){
13036             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13037         },
13038
13039         /**
13040          * Trims any whitespace from either side of a string
13041          * @param {String} value The text to trim
13042          * @return {String} The trimmed text
13043          */
13044         trim : function(value){
13045             return String(value).replace(trimRe, "");
13046         },
13047
13048         /**
13049          * Returns a substring from within an original string
13050          * @param {String} value The original text
13051          * @param {Number} start The start index of the substring
13052          * @param {Number} length The length of the substring
13053          * @return {String} The substring
13054          */
13055         substr : function(value, start, length){
13056             return String(value).substr(start, length);
13057         },
13058
13059         /**
13060          * Converts a string to all lower case letters
13061          * @param {String} value The text to convert
13062          * @return {String} The converted text
13063          */
13064         lowercase : function(value){
13065             return String(value).toLowerCase();
13066         },
13067
13068         /**
13069          * Converts a string to all upper case letters
13070          * @param {String} value The text to convert
13071          * @return {String} The converted text
13072          */
13073         uppercase : function(value){
13074             return String(value).toUpperCase();
13075         },
13076
13077         /**
13078          * Converts the first character only of a string to upper case
13079          * @param {String} value The text to convert
13080          * @return {String} The converted text
13081          */
13082         capitalize : function(value){
13083             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13084         },
13085
13086         // private
13087         call : function(value, fn){
13088             if(arguments.length > 2){
13089                 var args = Array.prototype.slice.call(arguments, 2);
13090                 args.unshift(value);
13091                  
13092                 return /** eval:var:value */  eval(fn).apply(window, args);
13093             }else{
13094                 /** eval:var:value */
13095                 return /** eval:var:value */ eval(fn).call(window, value);
13096             }
13097         },
13098
13099        
13100         /**
13101          * safer version of Math.toFixed..??/
13102          * @param {Number/String} value The numeric value to format
13103          * @param {Number/String} value Decimal places 
13104          * @return {String} The formatted currency string
13105          */
13106         toFixed : function(v, n)
13107         {
13108             // why not use to fixed - precision is buggered???
13109             if (!n) {
13110                 return Math.round(v-0);
13111             }
13112             var fact = Math.pow(10,n+1);
13113             v = (Math.round((v-0)*fact))/fact;
13114             var z = (''+fact).substring(2);
13115             if (v == Math.floor(v)) {
13116                 return Math.floor(v) + '.' + z;
13117             }
13118             
13119             // now just padd decimals..
13120             var ps = String(v).split('.');
13121             var fd = (ps[1] + z);
13122             var r = fd.substring(0,n); 
13123             var rm = fd.substring(n); 
13124             if (rm < 5) {
13125                 return ps[0] + '.' + r;
13126             }
13127             r*=1; // turn it into a number;
13128             r++;
13129             if (String(r).length != n) {
13130                 ps[0]*=1;
13131                 ps[0]++;
13132                 r = String(r).substring(1); // chop the end off.
13133             }
13134             
13135             return ps[0] + '.' + r;
13136              
13137         },
13138         
13139         /**
13140          * Format a number as US currency
13141          * @param {Number/String} value The numeric value to format
13142          * @return {String} The formatted currency string
13143          */
13144         usMoney : function(v){
13145             v = (Math.round((v-0)*100))/100;
13146             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13147             v = String(v);
13148             var ps = v.split('.');
13149             var whole = ps[0];
13150             var sub = ps[1] ? '.'+ ps[1] : '.00';
13151             var r = /(\d+)(\d{3})/;
13152             while (r.test(whole)) {
13153                 whole = whole.replace(r, '$1' + ',' + '$2');
13154             }
13155             return "$" + whole + sub ;
13156         },
13157         
13158         /**
13159          * Parse a value into a formatted date using the specified format pattern.
13160          * @param {Mixed} value The value to format
13161          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13162          * @return {String} The formatted date string
13163          */
13164         date : function(v, format){
13165             if(!v){
13166                 return "";
13167             }
13168             if(!(v instanceof Date)){
13169                 v = new Date(Date.parse(v));
13170             }
13171             return v.dateFormat(format || "m/d/Y");
13172         },
13173
13174         /**
13175          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13176          * @param {String} format Any valid date format string
13177          * @return {Function} The date formatting function
13178          */
13179         dateRenderer : function(format){
13180             return function(v){
13181                 return Roo.util.Format.date(v, format);  
13182             };
13183         },
13184
13185         // private
13186         stripTagsRE : /<\/?[^>]+>/gi,
13187         
13188         /**
13189          * Strips all HTML tags
13190          * @param {Mixed} value The text from which to strip tags
13191          * @return {String} The stripped text
13192          */
13193         stripTags : function(v){
13194             return !v ? v : String(v).replace(this.stripTagsRE, "");
13195         }
13196     };
13197 }();/*
13198  * Based on:
13199  * Ext JS Library 1.1.1
13200  * Copyright(c) 2006-2007, Ext JS, LLC.
13201  *
13202  * Originally Released Under LGPL - original licence link has changed is not relivant.
13203  *
13204  * Fork - LGPL
13205  * <script type="text/javascript">
13206  */
13207
13208
13209  
13210
13211 /**
13212  * @class Roo.MasterTemplate
13213  * @extends Roo.Template
13214  * Provides a template that can have child templates. The syntax is:
13215 <pre><code>
13216 var t = new Roo.MasterTemplate(
13217         '&lt;select name="{name}"&gt;',
13218                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13219         '&lt;/select&gt;'
13220 );
13221 t.add('options', {value: 'foo', text: 'bar'});
13222 // or you can add multiple child elements in one shot
13223 t.addAll('options', [
13224     {value: 'foo', text: 'bar'},
13225     {value: 'foo2', text: 'bar2'},
13226     {value: 'foo3', text: 'bar3'}
13227 ]);
13228 // then append, applying the master template values
13229 t.append('my-form', {name: 'my-select'});
13230 </code></pre>
13231 * A name attribute for the child template is not required if you have only one child
13232 * template or you want to refer to them by index.
13233  */
13234 Roo.MasterTemplate = function(){
13235     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13236     this.originalHtml = this.html;
13237     var st = {};
13238     var m, re = this.subTemplateRe;
13239     re.lastIndex = 0;
13240     var subIndex = 0;
13241     while(m = re.exec(this.html)){
13242         var name = m[1], content = m[2];
13243         st[subIndex] = {
13244             name: name,
13245             index: subIndex,
13246             buffer: [],
13247             tpl : new Roo.Template(content)
13248         };
13249         if(name){
13250             st[name] = st[subIndex];
13251         }
13252         st[subIndex].tpl.compile();
13253         st[subIndex].tpl.call = this.call.createDelegate(this);
13254         subIndex++;
13255     }
13256     this.subCount = subIndex;
13257     this.subs = st;
13258 };
13259 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13260     /**
13261     * The regular expression used to match sub templates
13262     * @type RegExp
13263     * @property
13264     */
13265     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13266
13267     /**
13268      * Applies the passed values to a child template.
13269      * @param {String/Number} name (optional) The name or index of the child template
13270      * @param {Array/Object} values The values to be applied to the template
13271      * @return {MasterTemplate} this
13272      */
13273      add : function(name, values){
13274         if(arguments.length == 1){
13275             values = arguments[0];
13276             name = 0;
13277         }
13278         var s = this.subs[name];
13279         s.buffer[s.buffer.length] = s.tpl.apply(values);
13280         return this;
13281     },
13282
13283     /**
13284      * Applies all the passed values to a child template.
13285      * @param {String/Number} name (optional) The name or index of the child template
13286      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13287      * @param {Boolean} reset (optional) True to reset the template first
13288      * @return {MasterTemplate} this
13289      */
13290     fill : function(name, values, reset){
13291         var a = arguments;
13292         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13293             values = a[0];
13294             name = 0;
13295             reset = a[1];
13296         }
13297         if(reset){
13298             this.reset();
13299         }
13300         for(var i = 0, len = values.length; i < len; i++){
13301             this.add(name, values[i]);
13302         }
13303         return this;
13304     },
13305
13306     /**
13307      * Resets the template for reuse
13308      * @return {MasterTemplate} this
13309      */
13310      reset : function(){
13311         var s = this.subs;
13312         for(var i = 0; i < this.subCount; i++){
13313             s[i].buffer = [];
13314         }
13315         return this;
13316     },
13317
13318     applyTemplate : function(values){
13319         var s = this.subs;
13320         var replaceIndex = -1;
13321         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13322             return s[++replaceIndex].buffer.join("");
13323         });
13324         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13325     },
13326
13327     apply : function(){
13328         return this.applyTemplate.apply(this, arguments);
13329     },
13330
13331     compile : function(){return this;}
13332 });
13333
13334 /**
13335  * Alias for fill().
13336  * @method
13337  */
13338 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13339  /**
13340  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13341  * var tpl = Roo.MasterTemplate.from('element-id');
13342  * @param {String/HTMLElement} el
13343  * @param {Object} config
13344  * @static
13345  */
13346 Roo.MasterTemplate.from = function(el, config){
13347     el = Roo.getDom(el);
13348     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13349 };/*
13350  * Based on:
13351  * Ext JS Library 1.1.1
13352  * Copyright(c) 2006-2007, Ext JS, LLC.
13353  *
13354  * Originally Released Under LGPL - original licence link has changed is not relivant.
13355  *
13356  * Fork - LGPL
13357  * <script type="text/javascript">
13358  */
13359
13360  
13361 /**
13362  * @class Roo.util.CSS
13363  * Utility class for manipulating CSS rules
13364  * @singleton
13365  */
13366 Roo.util.CSS = function(){
13367         var rules = null;
13368         var doc = document;
13369
13370     var camelRe = /(-[a-z])/gi;
13371     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13372
13373    return {
13374    /**
13375     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13376     * tag and appended to the HEAD of the document.
13377     * @param {String|Object} cssText The text containing the css rules
13378     * @param {String} id An id to add to the stylesheet for later removal
13379     * @return {StyleSheet}
13380     */
13381     createStyleSheet : function(cssText, id){
13382         var ss;
13383         var head = doc.getElementsByTagName("head")[0];
13384         var nrules = doc.createElement("style");
13385         nrules.setAttribute("type", "text/css");
13386         if(id){
13387             nrules.setAttribute("id", id);
13388         }
13389         if (typeof(cssText) != 'string') {
13390             // support object maps..
13391             // not sure if this a good idea.. 
13392             // perhaps it should be merged with the general css handling
13393             // and handle js style props.
13394             var cssTextNew = [];
13395             for(var n in cssText) {
13396                 var citems = [];
13397                 for(var k in cssText[n]) {
13398                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13399                 }
13400                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13401                 
13402             }
13403             cssText = cssTextNew.join("\n");
13404             
13405         }
13406        
13407        
13408        if(Roo.isIE){
13409            head.appendChild(nrules);
13410            ss = nrules.styleSheet;
13411            ss.cssText = cssText;
13412        }else{
13413            try{
13414                 nrules.appendChild(doc.createTextNode(cssText));
13415            }catch(e){
13416                nrules.cssText = cssText; 
13417            }
13418            head.appendChild(nrules);
13419            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13420        }
13421        this.cacheStyleSheet(ss);
13422        return ss;
13423    },
13424
13425    /**
13426     * Removes a style or link tag by id
13427     * @param {String} id The id of the tag
13428     */
13429    removeStyleSheet : function(id){
13430        var existing = doc.getElementById(id);
13431        if(existing){
13432            existing.parentNode.removeChild(existing);
13433        }
13434    },
13435
13436    /**
13437     * Dynamically swaps an existing stylesheet reference for a new one
13438     * @param {String} id The id of an existing link tag to remove
13439     * @param {String} url The href of the new stylesheet to include
13440     */
13441    swapStyleSheet : function(id, url){
13442        this.removeStyleSheet(id);
13443        var ss = doc.createElement("link");
13444        ss.setAttribute("rel", "stylesheet");
13445        ss.setAttribute("type", "text/css");
13446        ss.setAttribute("id", id);
13447        ss.setAttribute("href", url);
13448        doc.getElementsByTagName("head")[0].appendChild(ss);
13449    },
13450    
13451    /**
13452     * Refresh the rule cache if you have dynamically added stylesheets
13453     * @return {Object} An object (hash) of rules indexed by selector
13454     */
13455    refreshCache : function(){
13456        return this.getRules(true);
13457    },
13458
13459    // private
13460    cacheStyleSheet : function(stylesheet){
13461        if(!rules){
13462            rules = {};
13463        }
13464        try{// try catch for cross domain access issue
13465            var ssRules = stylesheet.cssRules || stylesheet.rules;
13466            for(var j = ssRules.length-1; j >= 0; --j){
13467                rules[ssRules[j].selectorText] = ssRules[j];
13468            }
13469        }catch(e){}
13470    },
13471    
13472    /**
13473     * Gets all css rules for the document
13474     * @param {Boolean} refreshCache true to refresh the internal cache
13475     * @return {Object} An object (hash) of rules indexed by selector
13476     */
13477    getRules : function(refreshCache){
13478                 if(rules == null || refreshCache){
13479                         rules = {};
13480                         var ds = doc.styleSheets;
13481                         for(var i =0, len = ds.length; i < len; i++){
13482                             try{
13483                         this.cacheStyleSheet(ds[i]);
13484                     }catch(e){} 
13485                 }
13486                 }
13487                 return rules;
13488         },
13489         
13490         /**
13491     * Gets an an individual CSS rule by selector(s)
13492     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13493     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13494     * @return {CSSRule} The CSS rule or null if one is not found
13495     */
13496    getRule : function(selector, refreshCache){
13497                 var rs = this.getRules(refreshCache);
13498                 if(!(selector instanceof Array)){
13499                     return rs[selector];
13500                 }
13501                 for(var i = 0; i < selector.length; i++){
13502                         if(rs[selector[i]]){
13503                                 return rs[selector[i]];
13504                         }
13505                 }
13506                 return null;
13507         },
13508         
13509         
13510         /**
13511     * Updates a rule property
13512     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13513     * @param {String} property The css property
13514     * @param {String} value The new value for the property
13515     * @return {Boolean} true If a rule was found and updated
13516     */
13517    updateRule : function(selector, property, value){
13518                 if(!(selector instanceof Array)){
13519                         var rule = this.getRule(selector);
13520                         if(rule){
13521                                 rule.style[property.replace(camelRe, camelFn)] = value;
13522                                 return true;
13523                         }
13524                 }else{
13525                         for(var i = 0; i < selector.length; i++){
13526                                 if(this.updateRule(selector[i], property, value)){
13527                                         return true;
13528                                 }
13529                         }
13530                 }
13531                 return false;
13532         }
13533    };   
13534 }();/*
13535  * Based on:
13536  * Ext JS Library 1.1.1
13537  * Copyright(c) 2006-2007, Ext JS, LLC.
13538  *
13539  * Originally Released Under LGPL - original licence link has changed is not relivant.
13540  *
13541  * Fork - LGPL
13542  * <script type="text/javascript">
13543  */
13544
13545  
13546
13547 /**
13548  * @class Roo.util.ClickRepeater
13549  * @extends Roo.util.Observable
13550  * 
13551  * A wrapper class which can be applied to any element. Fires a "click" event while the
13552  * mouse is pressed. The interval between firings may be specified in the config but
13553  * defaults to 10 milliseconds.
13554  * 
13555  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13556  * 
13557  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13558  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13559  * Similar to an autorepeat key delay.
13560  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13561  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13562  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13563  *           "interval" and "delay" are ignored. "immediate" is honored.
13564  * @cfg {Boolean} preventDefault True to prevent the default click event
13565  * @cfg {Boolean} stopDefault True to stop the default click event
13566  * 
13567  * @history
13568  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13569  *     2007-02-02 jvs Renamed to ClickRepeater
13570  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13571  *
13572  *  @constructor
13573  * @param {String/HTMLElement/Element} el The element to listen on
13574  * @param {Object} config
13575  **/
13576 Roo.util.ClickRepeater = function(el, config)
13577 {
13578     this.el = Roo.get(el);
13579     this.el.unselectable();
13580
13581     Roo.apply(this, config);
13582
13583     this.addEvents({
13584     /**
13585      * @event mousedown
13586      * Fires when the mouse button is depressed.
13587      * @param {Roo.util.ClickRepeater} this
13588      */
13589         "mousedown" : true,
13590     /**
13591      * @event click
13592      * Fires on a specified interval during the time the element is pressed.
13593      * @param {Roo.util.ClickRepeater} this
13594      */
13595         "click" : true,
13596     /**
13597      * @event mouseup
13598      * Fires when the mouse key is released.
13599      * @param {Roo.util.ClickRepeater} this
13600      */
13601         "mouseup" : true
13602     });
13603
13604     this.el.on("mousedown", this.handleMouseDown, this);
13605     if(this.preventDefault || this.stopDefault){
13606         this.el.on("click", function(e){
13607             if(this.preventDefault){
13608                 e.preventDefault();
13609             }
13610             if(this.stopDefault){
13611                 e.stopEvent();
13612             }
13613         }, this);
13614     }
13615
13616     // allow inline handler
13617     if(this.handler){
13618         this.on("click", this.handler,  this.scope || this);
13619     }
13620
13621     Roo.util.ClickRepeater.superclass.constructor.call(this);
13622 };
13623
13624 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13625     interval : 20,
13626     delay: 250,
13627     preventDefault : true,
13628     stopDefault : false,
13629     timer : 0,
13630
13631     // private
13632     handleMouseDown : function(){
13633         clearTimeout(this.timer);
13634         this.el.blur();
13635         if(this.pressClass){
13636             this.el.addClass(this.pressClass);
13637         }
13638         this.mousedownTime = new Date();
13639
13640         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13641         this.el.on("mouseout", this.handleMouseOut, this);
13642
13643         this.fireEvent("mousedown", this);
13644         this.fireEvent("click", this);
13645         
13646         this.timer = this.click.defer(this.delay || this.interval, this);
13647     },
13648
13649     // private
13650     click : function(){
13651         this.fireEvent("click", this);
13652         this.timer = this.click.defer(this.getInterval(), this);
13653     },
13654
13655     // private
13656     getInterval: function(){
13657         if(!this.accelerate){
13658             return this.interval;
13659         }
13660         var pressTime = this.mousedownTime.getElapsed();
13661         if(pressTime < 500){
13662             return 400;
13663         }else if(pressTime < 1700){
13664             return 320;
13665         }else if(pressTime < 2600){
13666             return 250;
13667         }else if(pressTime < 3500){
13668             return 180;
13669         }else if(pressTime < 4400){
13670             return 140;
13671         }else if(pressTime < 5300){
13672             return 80;
13673         }else if(pressTime < 6200){
13674             return 50;
13675         }else{
13676             return 10;
13677         }
13678     },
13679
13680     // private
13681     handleMouseOut : function(){
13682         clearTimeout(this.timer);
13683         if(this.pressClass){
13684             this.el.removeClass(this.pressClass);
13685         }
13686         this.el.on("mouseover", this.handleMouseReturn, this);
13687     },
13688
13689     // private
13690     handleMouseReturn : function(){
13691         this.el.un("mouseover", this.handleMouseReturn);
13692         if(this.pressClass){
13693             this.el.addClass(this.pressClass);
13694         }
13695         this.click();
13696     },
13697
13698     // private
13699     handleMouseUp : function(){
13700         clearTimeout(this.timer);
13701         this.el.un("mouseover", this.handleMouseReturn);
13702         this.el.un("mouseout", this.handleMouseOut);
13703         Roo.get(document).un("mouseup", this.handleMouseUp);
13704         this.el.removeClass(this.pressClass);
13705         this.fireEvent("mouseup", this);
13706     }
13707 });/*
13708  * Based on:
13709  * Ext JS Library 1.1.1
13710  * Copyright(c) 2006-2007, Ext JS, LLC.
13711  *
13712  * Originally Released Under LGPL - original licence link has changed is not relivant.
13713  *
13714  * Fork - LGPL
13715  * <script type="text/javascript">
13716  */
13717
13718  
13719 /**
13720  * @class Roo.KeyNav
13721  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13722  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13723  * way to implement custom navigation schemes for any UI component.</p>
13724  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13725  * pageUp, pageDown, del, home, end.  Usage:</p>
13726  <pre><code>
13727 var nav = new Roo.KeyNav("my-element", {
13728     "left" : function(e){
13729         this.moveLeft(e.ctrlKey);
13730     },
13731     "right" : function(e){
13732         this.moveRight(e.ctrlKey);
13733     },
13734     "enter" : function(e){
13735         this.save();
13736     },
13737     scope : this
13738 });
13739 </code></pre>
13740  * @constructor
13741  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13742  * @param {Object} config The config
13743  */
13744 Roo.KeyNav = function(el, config){
13745     this.el = Roo.get(el);
13746     Roo.apply(this, config);
13747     if(!this.disabled){
13748         this.disabled = true;
13749         this.enable();
13750     }
13751 };
13752
13753 Roo.KeyNav.prototype = {
13754     /**
13755      * @cfg {Boolean} disabled
13756      * True to disable this KeyNav instance (defaults to false)
13757      */
13758     disabled : false,
13759     /**
13760      * @cfg {String} defaultEventAction
13761      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13762      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13763      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13764      */
13765     defaultEventAction: "stopEvent",
13766     /**
13767      * @cfg {Boolean} forceKeyDown
13768      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13769      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13770      * handle keydown instead of keypress.
13771      */
13772     forceKeyDown : false,
13773
13774     // private
13775     prepareEvent : function(e){
13776         var k = e.getKey();
13777         var h = this.keyToHandler[k];
13778         //if(h && this[h]){
13779         //    e.stopPropagation();
13780         //}
13781         if(Roo.isSafari && h && k >= 37 && k <= 40){
13782             e.stopEvent();
13783         }
13784     },
13785
13786     // private
13787     relay : function(e){
13788         var k = e.getKey();
13789         var h = this.keyToHandler[k];
13790         if(h && this[h]){
13791             if(this.doRelay(e, this[h], h) !== true){
13792                 e[this.defaultEventAction]();
13793             }
13794         }
13795     },
13796
13797     // private
13798     doRelay : function(e, h, hname){
13799         return h.call(this.scope || this, e);
13800     },
13801
13802     // possible handlers
13803     enter : false,
13804     left : false,
13805     right : false,
13806     up : false,
13807     down : false,
13808     tab : false,
13809     esc : false,
13810     pageUp : false,
13811     pageDown : false,
13812     del : false,
13813     home : false,
13814     end : false,
13815
13816     // quick lookup hash
13817     keyToHandler : {
13818         37 : "left",
13819         39 : "right",
13820         38 : "up",
13821         40 : "down",
13822         33 : "pageUp",
13823         34 : "pageDown",
13824         46 : "del",
13825         36 : "home",
13826         35 : "end",
13827         13 : "enter",
13828         27 : "esc",
13829         9  : "tab"
13830     },
13831
13832         /**
13833          * Enable this KeyNav
13834          */
13835         enable: function(){
13836                 if(this.disabled){
13837             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13838             // the EventObject will normalize Safari automatically
13839             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13840                 this.el.on("keydown", this.relay,  this);
13841             }else{
13842                 this.el.on("keydown", this.prepareEvent,  this);
13843                 this.el.on("keypress", this.relay,  this);
13844             }
13845                     this.disabled = false;
13846                 }
13847         },
13848
13849         /**
13850          * Disable this KeyNav
13851          */
13852         disable: function(){
13853                 if(!this.disabled){
13854                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13855                 this.el.un("keydown", this.relay);
13856             }else{
13857                 this.el.un("keydown", this.prepareEvent);
13858                 this.el.un("keypress", this.relay);
13859             }
13860                     this.disabled = true;
13861                 }
13862         }
13863 };/*
13864  * Based on:
13865  * Ext JS Library 1.1.1
13866  * Copyright(c) 2006-2007, Ext JS, LLC.
13867  *
13868  * Originally Released Under LGPL - original licence link has changed is not relivant.
13869  *
13870  * Fork - LGPL
13871  * <script type="text/javascript">
13872  */
13873
13874  
13875 /**
13876  * @class Roo.KeyMap
13877  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13878  * The constructor accepts the same config object as defined by {@link #addBinding}.
13879  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13880  * combination it will call the function with this signature (if the match is a multi-key
13881  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13882  * A KeyMap can also handle a string representation of keys.<br />
13883  * Usage:
13884  <pre><code>
13885 // map one key by key code
13886 var map = new Roo.KeyMap("my-element", {
13887     key: 13, // or Roo.EventObject.ENTER
13888     fn: myHandler,
13889     scope: myObject
13890 });
13891
13892 // map multiple keys to one action by string
13893 var map = new Roo.KeyMap("my-element", {
13894     key: "a\r\n\t",
13895     fn: myHandler,
13896     scope: myObject
13897 });
13898
13899 // map multiple keys to multiple actions by strings and array of codes
13900 var map = new Roo.KeyMap("my-element", [
13901     {
13902         key: [10,13],
13903         fn: function(){ alert("Return was pressed"); }
13904     }, {
13905         key: "abc",
13906         fn: function(){ alert('a, b or c was pressed'); }
13907     }, {
13908         key: "\t",
13909         ctrl:true,
13910         shift:true,
13911         fn: function(){ alert('Control + shift + tab was pressed.'); }
13912     }
13913 ]);
13914 </code></pre>
13915  * <b>Note: A KeyMap starts enabled</b>
13916  * @constructor
13917  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13918  * @param {Object} config The config (see {@link #addBinding})
13919  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13920  */
13921 Roo.KeyMap = function(el, config, eventName){
13922     this.el  = Roo.get(el);
13923     this.eventName = eventName || "keydown";
13924     this.bindings = [];
13925     if(config){
13926         this.addBinding(config);
13927     }
13928     this.enable();
13929 };
13930
13931 Roo.KeyMap.prototype = {
13932     /**
13933      * True to stop the event from bubbling and prevent the default browser action if the
13934      * key was handled by the KeyMap (defaults to false)
13935      * @type Boolean
13936      */
13937     stopEvent : false,
13938
13939     /**
13940      * Add a new binding to this KeyMap. The following config object properties are supported:
13941      * <pre>
13942 Property    Type             Description
13943 ----------  ---------------  ----------------------------------------------------------------------
13944 key         String/Array     A single keycode or an array of keycodes to handle
13945 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13946 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13947 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13948 fn          Function         The function to call when KeyMap finds the expected key combination
13949 scope       Object           The scope of the callback function
13950 </pre>
13951      *
13952      * Usage:
13953      * <pre><code>
13954 // Create a KeyMap
13955 var map = new Roo.KeyMap(document, {
13956     key: Roo.EventObject.ENTER,
13957     fn: handleKey,
13958     scope: this
13959 });
13960
13961 //Add a new binding to the existing KeyMap later
13962 map.addBinding({
13963     key: 'abc',
13964     shift: true,
13965     fn: handleKey,
13966     scope: this
13967 });
13968 </code></pre>
13969      * @param {Object/Array} config A single KeyMap config or an array of configs
13970      */
13971         addBinding : function(config){
13972         if(config instanceof Array){
13973             for(var i = 0, len = config.length; i < len; i++){
13974                 this.addBinding(config[i]);
13975             }
13976             return;
13977         }
13978         var keyCode = config.key,
13979             shift = config.shift, 
13980             ctrl = config.ctrl, 
13981             alt = config.alt,
13982             fn = config.fn,
13983             scope = config.scope;
13984         if(typeof keyCode == "string"){
13985             var ks = [];
13986             var keyString = keyCode.toUpperCase();
13987             for(var j = 0, len = keyString.length; j < len; j++){
13988                 ks.push(keyString.charCodeAt(j));
13989             }
13990             keyCode = ks;
13991         }
13992         var keyArray = keyCode instanceof Array;
13993         var handler = function(e){
13994             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13995                 var k = e.getKey();
13996                 if(keyArray){
13997                     for(var i = 0, len = keyCode.length; i < len; i++){
13998                         if(keyCode[i] == k){
13999                           if(this.stopEvent){
14000                               e.stopEvent();
14001                           }
14002                           fn.call(scope || window, k, e);
14003                           return;
14004                         }
14005                     }
14006                 }else{
14007                     if(k == keyCode){
14008                         if(this.stopEvent){
14009                            e.stopEvent();
14010                         }
14011                         fn.call(scope || window, k, e);
14012                     }
14013                 }
14014             }
14015         };
14016         this.bindings.push(handler);  
14017         },
14018
14019     /**
14020      * Shorthand for adding a single key listener
14021      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14022      * following options:
14023      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14024      * @param {Function} fn The function to call
14025      * @param {Object} scope (optional) The scope of the function
14026      */
14027     on : function(key, fn, scope){
14028         var keyCode, shift, ctrl, alt;
14029         if(typeof key == "object" && !(key instanceof Array)){
14030             keyCode = key.key;
14031             shift = key.shift;
14032             ctrl = key.ctrl;
14033             alt = key.alt;
14034         }else{
14035             keyCode = key;
14036         }
14037         this.addBinding({
14038             key: keyCode,
14039             shift: shift,
14040             ctrl: ctrl,
14041             alt: alt,
14042             fn: fn,
14043             scope: scope
14044         })
14045     },
14046
14047     // private
14048     handleKeyDown : function(e){
14049             if(this.enabled){ //just in case
14050             var b = this.bindings;
14051             for(var i = 0, len = b.length; i < len; i++){
14052                 b[i].call(this, e);
14053             }
14054             }
14055         },
14056         
14057         /**
14058          * Returns true if this KeyMap is enabled
14059          * @return {Boolean} 
14060          */
14061         isEnabled : function(){
14062             return this.enabled;  
14063         },
14064         
14065         /**
14066          * Enables this KeyMap
14067          */
14068         enable: function(){
14069                 if(!this.enabled){
14070                     this.el.on(this.eventName, this.handleKeyDown, this);
14071                     this.enabled = true;
14072                 }
14073         },
14074
14075         /**
14076          * Disable this KeyMap
14077          */
14078         disable: function(){
14079                 if(this.enabled){
14080                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14081                     this.enabled = false;
14082                 }
14083         }
14084 };/*
14085  * Based on:
14086  * Ext JS Library 1.1.1
14087  * Copyright(c) 2006-2007, Ext JS, LLC.
14088  *
14089  * Originally Released Under LGPL - original licence link has changed is not relivant.
14090  *
14091  * Fork - LGPL
14092  * <script type="text/javascript">
14093  */
14094
14095  
14096 /**
14097  * @class Roo.util.TextMetrics
14098  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14099  * wide, in pixels, a given block of text will be.
14100  * @singleton
14101  */
14102 Roo.util.TextMetrics = function(){
14103     var shared;
14104     return {
14105         /**
14106          * Measures the size of the specified text
14107          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14108          * that can affect the size of the rendered text
14109          * @param {String} text The text to measure
14110          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14111          * in order to accurately measure the text height
14112          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14113          */
14114         measure : function(el, text, fixedWidth){
14115             if(!shared){
14116                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14117             }
14118             shared.bind(el);
14119             shared.setFixedWidth(fixedWidth || 'auto');
14120             return shared.getSize(text);
14121         },
14122
14123         /**
14124          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14125          * the overhead of multiple calls to initialize the style properties on each measurement.
14126          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14127          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14128          * in order to accurately measure the text height
14129          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14130          */
14131         createInstance : function(el, fixedWidth){
14132             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14133         }
14134     };
14135 }();
14136
14137  
14138
14139 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14140     var ml = new Roo.Element(document.createElement('div'));
14141     document.body.appendChild(ml.dom);
14142     ml.position('absolute');
14143     ml.setLeftTop(-1000, -1000);
14144     ml.hide();
14145
14146     if(fixedWidth){
14147         ml.setWidth(fixedWidth);
14148     }
14149      
14150     var instance = {
14151         /**
14152          * Returns the size of the specified text based on the internal element's style and width properties
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {String} text The text to measure
14155          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14156          */
14157         getSize : function(text){
14158             ml.update(text);
14159             var s = ml.getSize();
14160             ml.update('');
14161             return s;
14162         },
14163
14164         /**
14165          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14166          * that can affect the size of the rendered text
14167          * @memberOf Roo.util.TextMetrics.Instance#
14168          * @param {String/HTMLElement} el The element, dom node or id
14169          */
14170         bind : function(el){
14171             ml.setStyle(
14172                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14173             );
14174         },
14175
14176         /**
14177          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14178          * to set a fixed width in order to accurately measure the text height.
14179          * @memberOf Roo.util.TextMetrics.Instance#
14180          * @param {Number} width The width to set on the element
14181          */
14182         setFixedWidth : function(width){
14183             ml.setWidth(width);
14184         },
14185
14186         /**
14187          * Returns the measured width of the specified text
14188          * @memberOf Roo.util.TextMetrics.Instance#
14189          * @param {String} text The text to measure
14190          * @return {Number} width The width in pixels
14191          */
14192         getWidth : function(text){
14193             ml.dom.style.width = 'auto';
14194             return this.getSize(text).width;
14195         },
14196
14197         /**
14198          * Returns the measured height of the specified text.  For multiline text, be sure to call
14199          * {@link #setFixedWidth} if necessary.
14200          * @memberOf Roo.util.TextMetrics.Instance#
14201          * @param {String} text The text to measure
14202          * @return {Number} height The height in pixels
14203          */
14204         getHeight : function(text){
14205             return this.getSize(text).height;
14206         }
14207     };
14208
14209     instance.bind(bindTo);
14210
14211     return instance;
14212 };
14213
14214 // backwards compat
14215 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14216  * Based on:
14217  * Ext JS Library 1.1.1
14218  * Copyright(c) 2006-2007, Ext JS, LLC.
14219  *
14220  * Originally Released Under LGPL - original licence link has changed is not relivant.
14221  *
14222  * Fork - LGPL
14223  * <script type="text/javascript">
14224  */
14225
14226 /**
14227  * @class Roo.state.Provider
14228  * Abstract base class for state provider implementations. This class provides methods
14229  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14230  * Provider interface.
14231  */
14232 Roo.state.Provider = function(){
14233     /**
14234      * @event statechange
14235      * Fires when a state change occurs.
14236      * @param {Provider} this This state provider
14237      * @param {String} key The state key which was changed
14238      * @param {String} value The encoded value for the state
14239      */
14240     this.addEvents({
14241         "statechange": true
14242     });
14243     this.state = {};
14244     Roo.state.Provider.superclass.constructor.call(this);
14245 };
14246 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14247     /**
14248      * Returns the current value for a key
14249      * @param {String} name The key name
14250      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14251      * @return {Mixed} The state data
14252      */
14253     get : function(name, defaultValue){
14254         return typeof this.state[name] == "undefined" ?
14255             defaultValue : this.state[name];
14256     },
14257     
14258     /**
14259      * Clears a value from the state
14260      * @param {String} name The key name
14261      */
14262     clear : function(name){
14263         delete this.state[name];
14264         this.fireEvent("statechange", this, name, null);
14265     },
14266     
14267     /**
14268      * Sets the value for a key
14269      * @param {String} name The key name
14270      * @param {Mixed} value The value to set
14271      */
14272     set : function(name, value){
14273         this.state[name] = value;
14274         this.fireEvent("statechange", this, name, value);
14275     },
14276     
14277     /**
14278      * Decodes a string previously encoded with {@link #encodeValue}.
14279      * @param {String} value The value to decode
14280      * @return {Mixed} The decoded value
14281      */
14282     decodeValue : function(cookie){
14283         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14284         var matches = re.exec(unescape(cookie));
14285         if(!matches || !matches[1]) return; // non state cookie
14286         var type = matches[1];
14287         var v = matches[2];
14288         switch(type){
14289             case "n":
14290                 return parseFloat(v);
14291             case "d":
14292                 return new Date(Date.parse(v));
14293             case "b":
14294                 return (v == "1");
14295             case "a":
14296                 var all = [];
14297                 var values = v.split("^");
14298                 for(var i = 0, len = values.length; i < len; i++){
14299                     all.push(this.decodeValue(values[i]));
14300                 }
14301                 return all;
14302            case "o":
14303                 var all = {};
14304                 var values = v.split("^");
14305                 for(var i = 0, len = values.length; i < len; i++){
14306                     var kv = values[i].split("=");
14307                     all[kv[0]] = this.decodeValue(kv[1]);
14308                 }
14309                 return all;
14310            default:
14311                 return v;
14312         }
14313     },
14314     
14315     /**
14316      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14317      * @param {Mixed} value The value to encode
14318      * @return {String} The encoded value
14319      */
14320     encodeValue : function(v){
14321         var enc;
14322         if(typeof v == "number"){
14323             enc = "n:" + v;
14324         }else if(typeof v == "boolean"){
14325             enc = "b:" + (v ? "1" : "0");
14326         }else if(v instanceof Date){
14327             enc = "d:" + v.toGMTString();
14328         }else if(v instanceof Array){
14329             var flat = "";
14330             for(var i = 0, len = v.length; i < len; i++){
14331                 flat += this.encodeValue(v[i]);
14332                 if(i != len-1) flat += "^";
14333             }
14334             enc = "a:" + flat;
14335         }else if(typeof v == "object"){
14336             var flat = "";
14337             for(var key in v){
14338                 if(typeof v[key] != "function"){
14339                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14340                 }
14341             }
14342             enc = "o:" + flat.substring(0, flat.length-1);
14343         }else{
14344             enc = "s:" + v;
14345         }
14346         return escape(enc);        
14347     }
14348 });
14349
14350 /*
14351  * Based on:
14352  * Ext JS Library 1.1.1
14353  * Copyright(c) 2006-2007, Ext JS, LLC.
14354  *
14355  * Originally Released Under LGPL - original licence link has changed is not relivant.
14356  *
14357  * Fork - LGPL
14358  * <script type="text/javascript">
14359  */
14360 /**
14361  * @class Roo.state.Manager
14362  * This is the global state manager. By default all components that are "state aware" check this class
14363  * for state information if you don't pass them a custom state provider. In order for this class
14364  * to be useful, it must be initialized with a provider when your application initializes.
14365  <pre><code>
14366 // in your initialization function
14367 init : function(){
14368    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14369    ...
14370    // supposed you have a {@link Roo.BorderLayout}
14371    var layout = new Roo.BorderLayout(...);
14372    layout.restoreState();
14373    // or a {Roo.BasicDialog}
14374    var dialog = new Roo.BasicDialog(...);
14375    dialog.restoreState();
14376  </code></pre>
14377  * @singleton
14378  */
14379 Roo.state.Manager = function(){
14380     var provider = new Roo.state.Provider();
14381     
14382     return {
14383         /**
14384          * Configures the default state provider for your application
14385          * @param {Provider} stateProvider The state provider to set
14386          */
14387         setProvider : function(stateProvider){
14388             provider = stateProvider;
14389         },
14390         
14391         /**
14392          * Returns the current value for a key
14393          * @param {String} name The key name
14394          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14395          * @return {Mixed} The state data
14396          */
14397         get : function(key, defaultValue){
14398             return provider.get(key, defaultValue);
14399         },
14400         
14401         /**
14402          * Sets the value for a key
14403          * @param {String} name The key name
14404          * @param {Mixed} value The state data
14405          */
14406          set : function(key, value){
14407             provider.set(key, value);
14408         },
14409         
14410         /**
14411          * Clears a value from the state
14412          * @param {String} name The key name
14413          */
14414         clear : function(key){
14415             provider.clear(key);
14416         },
14417         
14418         /**
14419          * Gets the currently configured state provider
14420          * @return {Provider} The state provider
14421          */
14422         getProvider : function(){
14423             return provider;
14424         }
14425     };
14426 }();
14427 /*
14428  * Based on:
14429  * Ext JS Library 1.1.1
14430  * Copyright(c) 2006-2007, Ext JS, LLC.
14431  *
14432  * Originally Released Under LGPL - original licence link has changed is not relivant.
14433  *
14434  * Fork - LGPL
14435  * <script type="text/javascript">
14436  */
14437 /**
14438  * @class Roo.state.CookieProvider
14439  * @extends Roo.state.Provider
14440  * The default Provider implementation which saves state via cookies.
14441  * <br />Usage:
14442  <pre><code>
14443    var cp = new Roo.state.CookieProvider({
14444        path: "/cgi-bin/",
14445        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14446        domain: "roojs.com"
14447    })
14448    Roo.state.Manager.setProvider(cp);
14449  </code></pre>
14450  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14451  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14452  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14453  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14454  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14455  * domain the page is running on including the 'www' like 'www.roojs.com')
14456  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14457  * @constructor
14458  * Create a new CookieProvider
14459  * @param {Object} config The configuration object
14460  */
14461 Roo.state.CookieProvider = function(config){
14462     Roo.state.CookieProvider.superclass.constructor.call(this);
14463     this.path = "/";
14464     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14465     this.domain = null;
14466     this.secure = false;
14467     Roo.apply(this, config);
14468     this.state = this.readCookies();
14469 };
14470
14471 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14472     // private
14473     set : function(name, value){
14474         if(typeof value == "undefined" || value === null){
14475             this.clear(name);
14476             return;
14477         }
14478         this.setCookie(name, value);
14479         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14480     },
14481
14482     // private
14483     clear : function(name){
14484         this.clearCookie(name);
14485         Roo.state.CookieProvider.superclass.clear.call(this, name);
14486     },
14487
14488     // private
14489     readCookies : function(){
14490         var cookies = {};
14491         var c = document.cookie + ";";
14492         var re = /\s?(.*?)=(.*?);/g;
14493         var matches;
14494         while((matches = re.exec(c)) != null){
14495             var name = matches[1];
14496             var value = matches[2];
14497             if(name && name.substring(0,3) == "ys-"){
14498                 cookies[name.substr(3)] = this.decodeValue(value);
14499             }
14500         }
14501         return cookies;
14502     },
14503
14504     // private
14505     setCookie : function(name, value){
14506         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14507            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14508            ((this.path == null) ? "" : ("; path=" + this.path)) +
14509            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14510            ((this.secure == true) ? "; secure" : "");
14511     },
14512
14513     // private
14514     clearCookie : function(name){
14515         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14516            ((this.path == null) ? "" : ("; path=" + this.path)) +
14517            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14518            ((this.secure == true) ? "; secure" : "");
14519     }
14520 });/*
14521  * Based on:
14522  * Ext JS Library 1.1.1
14523  * Copyright(c) 2006-2007, Ext JS, LLC.
14524  *
14525  * Originally Released Under LGPL - original licence link has changed is not relivant.
14526  *
14527  * Fork - LGPL
14528  * <script type="text/javascript">
14529  */
14530
14531
14532
14533 /*
14534  * These classes are derivatives of the similarly named classes in the YUI Library.
14535  * The original license:
14536  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14537  * Code licensed under the BSD License:
14538  * http://developer.yahoo.net/yui/license.txt
14539  */
14540
14541 (function() {
14542
14543 var Event=Roo.EventManager;
14544 var Dom=Roo.lib.Dom;
14545
14546 /**
14547  * @class Roo.dd.DragDrop
14548  * @extends Roo.util.Observable
14549  * Defines the interface and base operation of items that that can be
14550  * dragged or can be drop targets.  It was designed to be extended, overriding
14551  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14552  * Up to three html elements can be associated with a DragDrop instance:
14553  * <ul>
14554  * <li>linked element: the element that is passed into the constructor.
14555  * This is the element which defines the boundaries for interaction with
14556  * other DragDrop objects.</li>
14557  * <li>handle element(s): The drag operation only occurs if the element that
14558  * was clicked matches a handle element.  By default this is the linked
14559  * element, but there are times that you will want only a portion of the
14560  * linked element to initiate the drag operation, and the setHandleElId()
14561  * method provides a way to define this.</li>
14562  * <li>drag element: this represents the element that would be moved along
14563  * with the cursor during a drag operation.  By default, this is the linked
14564  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14565  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14566  * </li>
14567  * </ul>
14568  * This class should not be instantiated until the onload event to ensure that
14569  * the associated elements are available.
14570  * The following would define a DragDrop obj that would interact with any
14571  * other DragDrop obj in the "group1" group:
14572  * <pre>
14573  *  dd = new Roo.dd.DragDrop("div1", "group1");
14574  * </pre>
14575  * Since none of the event handlers have been implemented, nothing would
14576  * actually happen if you were to run the code above.  Normally you would
14577  * override this class or one of the default implementations, but you can
14578  * also override the methods you want on an instance of the class...
14579  * <pre>
14580  *  dd.onDragDrop = function(e, id) {
14581  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14582  *  }
14583  * </pre>
14584  * @constructor
14585  * @param {String} id of the element that is linked to this instance
14586  * @param {String} sGroup the group of related DragDrop objects
14587  * @param {object} config an object containing configurable attributes
14588  *                Valid properties for DragDrop:
14589  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14590  */
14591 Roo.dd.DragDrop = function(id, sGroup, config) {
14592     if (id) {
14593         this.init(id, sGroup, config);
14594     }
14595     
14596 };
14597
14598 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14599
14600     /**
14601      * The id of the element associated with this object.  This is what we
14602      * refer to as the "linked element" because the size and position of
14603      * this element is used to determine when the drag and drop objects have
14604      * interacted.
14605      * @property id
14606      * @type String
14607      */
14608     id: null,
14609
14610     /**
14611      * Configuration attributes passed into the constructor
14612      * @property config
14613      * @type object
14614      */
14615     config: null,
14616
14617     /**
14618      * The id of the element that will be dragged.  By default this is same
14619      * as the linked element , but could be changed to another element. Ex:
14620      * Roo.dd.DDProxy
14621      * @property dragElId
14622      * @type String
14623      * @private
14624      */
14625     dragElId: null,
14626
14627     /**
14628      * the id of the element that initiates the drag operation.  By default
14629      * this is the linked element, but could be changed to be a child of this
14630      * element.  This lets us do things like only starting the drag when the
14631      * header element within the linked html element is clicked.
14632      * @property handleElId
14633      * @type String
14634      * @private
14635      */
14636     handleElId: null,
14637
14638     /**
14639      * An associative array of HTML tags that will be ignored if clicked.
14640      * @property invalidHandleTypes
14641      * @type {string: string}
14642      */
14643     invalidHandleTypes: null,
14644
14645     /**
14646      * An associative array of ids for elements that will be ignored if clicked
14647      * @property invalidHandleIds
14648      * @type {string: string}
14649      */
14650     invalidHandleIds: null,
14651
14652     /**
14653      * An indexted array of css class names for elements that will be ignored
14654      * if clicked.
14655      * @property invalidHandleClasses
14656      * @type string[]
14657      */
14658     invalidHandleClasses: null,
14659
14660     /**
14661      * The linked element's absolute X position at the time the drag was
14662      * started
14663      * @property startPageX
14664      * @type int
14665      * @private
14666      */
14667     startPageX: 0,
14668
14669     /**
14670      * The linked element's absolute X position at the time the drag was
14671      * started
14672      * @property startPageY
14673      * @type int
14674      * @private
14675      */
14676     startPageY: 0,
14677
14678     /**
14679      * The group defines a logical collection of DragDrop objects that are
14680      * related.  Instances only get events when interacting with other
14681      * DragDrop object in the same group.  This lets us define multiple
14682      * groups using a single DragDrop subclass if we want.
14683      * @property groups
14684      * @type {string: string}
14685      */
14686     groups: null,
14687
14688     /**
14689      * Individual drag/drop instances can be locked.  This will prevent
14690      * onmousedown start drag.
14691      * @property locked
14692      * @type boolean
14693      * @private
14694      */
14695     locked: false,
14696
14697     /**
14698      * Lock this instance
14699      * @method lock
14700      */
14701     lock: function() { this.locked = true; },
14702
14703     /**
14704      * Unlock this instace
14705      * @method unlock
14706      */
14707     unlock: function() { this.locked = false; },
14708
14709     /**
14710      * By default, all insances can be a drop target.  This can be disabled by
14711      * setting isTarget to false.
14712      * @method isTarget
14713      * @type boolean
14714      */
14715     isTarget: true,
14716
14717     /**
14718      * The padding configured for this drag and drop object for calculating
14719      * the drop zone intersection with this object.
14720      * @method padding
14721      * @type int[]
14722      */
14723     padding: null,
14724
14725     /**
14726      * Cached reference to the linked element
14727      * @property _domRef
14728      * @private
14729      */
14730     _domRef: null,
14731
14732     /**
14733      * Internal typeof flag
14734      * @property __ygDragDrop
14735      * @private
14736      */
14737     __ygDragDrop: true,
14738
14739     /**
14740      * Set to true when horizontal contraints are applied
14741      * @property constrainX
14742      * @type boolean
14743      * @private
14744      */
14745     constrainX: false,
14746
14747     /**
14748      * Set to true when vertical contraints are applied
14749      * @property constrainY
14750      * @type boolean
14751      * @private
14752      */
14753     constrainY: false,
14754
14755     /**
14756      * The left constraint
14757      * @property minX
14758      * @type int
14759      * @private
14760      */
14761     minX: 0,
14762
14763     /**
14764      * The right constraint
14765      * @property maxX
14766      * @type int
14767      * @private
14768      */
14769     maxX: 0,
14770
14771     /**
14772      * The up constraint
14773      * @property minY
14774      * @type int
14775      * @type int
14776      * @private
14777      */
14778     minY: 0,
14779
14780     /**
14781      * The down constraint
14782      * @property maxY
14783      * @type int
14784      * @private
14785      */
14786     maxY: 0,
14787
14788     /**
14789      * Maintain offsets when we resetconstraints.  Set to true when you want
14790      * the position of the element relative to its parent to stay the same
14791      * when the page changes
14792      *
14793      * @property maintainOffset
14794      * @type boolean
14795      */
14796     maintainOffset: false,
14797
14798     /**
14799      * Array of pixel locations the element will snap to if we specified a
14800      * horizontal graduation/interval.  This array is generated automatically
14801      * when you define a tick interval.
14802      * @property xTicks
14803      * @type int[]
14804      */
14805     xTicks: null,
14806
14807     /**
14808      * Array of pixel locations the element will snap to if we specified a
14809      * vertical graduation/interval.  This array is generated automatically
14810      * when you define a tick interval.
14811      * @property yTicks
14812      * @type int[]
14813      */
14814     yTicks: null,
14815
14816     /**
14817      * By default the drag and drop instance will only respond to the primary
14818      * button click (left button for a right-handed mouse).  Set to true to
14819      * allow drag and drop to start with any mouse click that is propogated
14820      * by the browser
14821      * @property primaryButtonOnly
14822      * @type boolean
14823      */
14824     primaryButtonOnly: true,
14825
14826     /**
14827      * The availabe property is false until the linked dom element is accessible.
14828      * @property available
14829      * @type boolean
14830      */
14831     available: false,
14832
14833     /**
14834      * By default, drags can only be initiated if the mousedown occurs in the
14835      * region the linked element is.  This is done in part to work around a
14836      * bug in some browsers that mis-report the mousedown if the previous
14837      * mouseup happened outside of the window.  This property is set to true
14838      * if outer handles are defined.
14839      *
14840      * @property hasOuterHandles
14841      * @type boolean
14842      * @default false
14843      */
14844     hasOuterHandles: false,
14845
14846     /**
14847      * Code that executes immediately before the startDrag event
14848      * @method b4StartDrag
14849      * @private
14850      */
14851     b4StartDrag: function(x, y) { },
14852
14853     /**
14854      * Abstract method called after a drag/drop object is clicked
14855      * and the drag or mousedown time thresholds have beeen met.
14856      * @method startDrag
14857      * @param {int} X click location
14858      * @param {int} Y click location
14859      */
14860     startDrag: function(x, y) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDrag event
14864      * @method b4Drag
14865      * @private
14866      */
14867     b4Drag: function(e) { },
14868
14869     /**
14870      * Abstract method called during the onMouseMove event while dragging an
14871      * object.
14872      * @method onDrag
14873      * @param {Event} e the mousemove event
14874      */
14875     onDrag: function(e) { /* override this */ },
14876
14877     /**
14878      * Abstract method called when this element fist begins hovering over
14879      * another DragDrop obj
14880      * @method onDragEnter
14881      * @param {Event} e the mousemove event
14882      * @param {String|DragDrop[]} id In POINT mode, the element
14883      * id this is hovering over.  In INTERSECT mode, an array of one or more
14884      * dragdrop items being hovered over.
14885      */
14886     onDragEnter: function(e, id) { /* override this */ },
14887
14888     /**
14889      * Code that executes immediately before the onDragOver event
14890      * @method b4DragOver
14891      * @private
14892      */
14893     b4DragOver: function(e) { },
14894
14895     /**
14896      * Abstract method called when this element is hovering over another
14897      * DragDrop obj
14898      * @method onDragOver
14899      * @param {Event} e the mousemove event
14900      * @param {String|DragDrop[]} id In POINT mode, the element
14901      * id this is hovering over.  In INTERSECT mode, an array of dd items
14902      * being hovered over.
14903      */
14904     onDragOver: function(e, id) { /* override this */ },
14905
14906     /**
14907      * Code that executes immediately before the onDragOut event
14908      * @method b4DragOut
14909      * @private
14910      */
14911     b4DragOut: function(e) { },
14912
14913     /**
14914      * Abstract method called when we are no longer hovering over an element
14915      * @method onDragOut
14916      * @param {Event} e the mousemove event
14917      * @param {String|DragDrop[]} id In POINT mode, the element
14918      * id this was hovering over.  In INTERSECT mode, an array of dd items
14919      * that the mouse is no longer over.
14920      */
14921     onDragOut: function(e, id) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the onDragDrop event
14925      * @method b4DragDrop
14926      * @private
14927      */
14928     b4DragDrop: function(e) { },
14929
14930     /**
14931      * Abstract method called when this item is dropped on another DragDrop
14932      * obj
14933      * @method onDragDrop
14934      * @param {Event} e the mouseup event
14935      * @param {String|DragDrop[]} id In POINT mode, the element
14936      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14937      * was dropped on.
14938      */
14939     onDragDrop: function(e, id) { /* override this */ },
14940
14941     /**
14942      * Abstract method called when this item is dropped on an area with no
14943      * drop target
14944      * @method onInvalidDrop
14945      * @param {Event} e the mouseup event
14946      */
14947     onInvalidDrop: function(e) { /* override this */ },
14948
14949     /**
14950      * Code that executes immediately before the endDrag event
14951      * @method b4EndDrag
14952      * @private
14953      */
14954     b4EndDrag: function(e) { },
14955
14956     /**
14957      * Fired when we are done dragging the object
14958      * @method endDrag
14959      * @param {Event} e the mouseup event
14960      */
14961     endDrag: function(e) { /* override this */ },
14962
14963     /**
14964      * Code executed immediately before the onMouseDown event
14965      * @method b4MouseDown
14966      * @param {Event} e the mousedown event
14967      * @private
14968      */
14969     b4MouseDown: function(e) {  },
14970
14971     /**
14972      * Event handler that fires when a drag/drop obj gets a mousedown
14973      * @method onMouseDown
14974      * @param {Event} e the mousedown event
14975      */
14976     onMouseDown: function(e) { /* override this */ },
14977
14978     /**
14979      * Event handler that fires when a drag/drop obj gets a mouseup
14980      * @method onMouseUp
14981      * @param {Event} e the mouseup event
14982      */
14983     onMouseUp: function(e) { /* override this */ },
14984
14985     /**
14986      * Override the onAvailable method to do what is needed after the initial
14987      * position was determined.
14988      * @method onAvailable
14989      */
14990     onAvailable: function () {
14991     },
14992
14993     /*
14994      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14995      * @type Object
14996      */
14997     defaultPadding : {left:0, right:0, top:0, bottom:0},
14998
14999     /*
15000      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15001  *
15002  * Usage:
15003  <pre><code>
15004  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15005                 { dragElId: "existingProxyDiv" });
15006  dd.startDrag = function(){
15007      this.constrainTo("parent-id");
15008  };
15009  </code></pre>
15010  * Or you can initalize it using the {@link Roo.Element} object:
15011  <pre><code>
15012  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15013      startDrag : function(){
15014          this.constrainTo("parent-id");
15015      }
15016  });
15017  </code></pre>
15018      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15019      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15020      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15021      * an object containing the sides to pad. For example: {right:10, bottom:10}
15022      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15023      */
15024     constrainTo : function(constrainTo, pad, inContent){
15025         if(typeof pad == "number"){
15026             pad = {left: pad, right:pad, top:pad, bottom:pad};
15027         }
15028         pad = pad || this.defaultPadding;
15029         var b = Roo.get(this.getEl()).getBox();
15030         var ce = Roo.get(constrainTo);
15031         var s = ce.getScroll();
15032         var c, cd = ce.dom;
15033         if(cd == document.body){
15034             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15035         }else{
15036             xy = ce.getXY();
15037             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15038         }
15039
15040
15041         var topSpace = b.y - c.y;
15042         var leftSpace = b.x - c.x;
15043
15044         this.resetConstraints();
15045         this.setXConstraint(leftSpace - (pad.left||0), // left
15046                 c.width - leftSpace - b.width - (pad.right||0) //right
15047         );
15048         this.setYConstraint(topSpace - (pad.top||0), //top
15049                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15050         );
15051     },
15052
15053     /**
15054      * Returns a reference to the linked element
15055      * @method getEl
15056      * @return {HTMLElement} the html element
15057      */
15058     getEl: function() {
15059         if (!this._domRef) {
15060             this._domRef = Roo.getDom(this.id);
15061         }
15062
15063         return this._domRef;
15064     },
15065
15066     /**
15067      * Returns a reference to the actual element to drag.  By default this is
15068      * the same as the html element, but it can be assigned to another
15069      * element. An example of this can be found in Roo.dd.DDProxy
15070      * @method getDragEl
15071      * @return {HTMLElement} the html element
15072      */
15073     getDragEl: function() {
15074         return Roo.getDom(this.dragElId);
15075     },
15076
15077     /**
15078      * Sets up the DragDrop object.  Must be called in the constructor of any
15079      * Roo.dd.DragDrop subclass
15080      * @method init
15081      * @param id the id of the linked element
15082      * @param {String} sGroup the group of related items
15083      * @param {object} config configuration attributes
15084      */
15085     init: function(id, sGroup, config) {
15086         this.initTarget(id, sGroup, config);
15087         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15088         // Event.on(this.id, "selectstart", Event.preventDefault);
15089     },
15090
15091     /**
15092      * Initializes Targeting functionality only... the object does not
15093      * get a mousedown handler.
15094      * @method initTarget
15095      * @param id the id of the linked element
15096      * @param {String} sGroup the group of related items
15097      * @param {object} config configuration attributes
15098      */
15099     initTarget: function(id, sGroup, config) {
15100
15101         // configuration attributes
15102         this.config = config || {};
15103
15104         // create a local reference to the drag and drop manager
15105         this.DDM = Roo.dd.DDM;
15106         // initialize the groups array
15107         this.groups = {};
15108
15109         // assume that we have an element reference instead of an id if the
15110         // parameter is not a string
15111         if (typeof id !== "string") {
15112             id = Roo.id(id);
15113         }
15114
15115         // set the id
15116         this.id = id;
15117
15118         // add to an interaction group
15119         this.addToGroup((sGroup) ? sGroup : "default");
15120
15121         // We don't want to register this as the handle with the manager
15122         // so we just set the id rather than calling the setter.
15123         this.handleElId = id;
15124
15125         // the linked element is the element that gets dragged by default
15126         this.setDragElId(id);
15127
15128         // by default, clicked anchors will not start drag operations.
15129         this.invalidHandleTypes = { A: "A" };
15130         this.invalidHandleIds = {};
15131         this.invalidHandleClasses = [];
15132
15133         this.applyConfig();
15134
15135         this.handleOnAvailable();
15136     },
15137
15138     /**
15139      * Applies the configuration parameters that were passed into the constructor.
15140      * This is supposed to happen at each level through the inheritance chain.  So
15141      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15142      * DragDrop in order to get all of the parameters that are available in
15143      * each object.
15144      * @method applyConfig
15145      */
15146     applyConfig: function() {
15147
15148         // configurable properties:
15149         //    padding, isTarget, maintainOffset, primaryButtonOnly
15150         this.padding           = this.config.padding || [0, 0, 0, 0];
15151         this.isTarget          = (this.config.isTarget !== false);
15152         this.maintainOffset    = (this.config.maintainOffset);
15153         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15154
15155     },
15156
15157     /**
15158      * Executed when the linked element is available
15159      * @method handleOnAvailable
15160      * @private
15161      */
15162     handleOnAvailable: function() {
15163         this.available = true;
15164         this.resetConstraints();
15165         this.onAvailable();
15166     },
15167
15168      /**
15169      * Configures the padding for the target zone in px.  Effectively expands
15170      * (or reduces) the virtual object size for targeting calculations.
15171      * Supports css-style shorthand; if only one parameter is passed, all sides
15172      * will have that padding, and if only two are passed, the top and bottom
15173      * will have the first param, the left and right the second.
15174      * @method setPadding
15175      * @param {int} iTop    Top pad
15176      * @param {int} iRight  Right pad
15177      * @param {int} iBot    Bot pad
15178      * @param {int} iLeft   Left pad
15179      */
15180     setPadding: function(iTop, iRight, iBot, iLeft) {
15181         // this.padding = [iLeft, iRight, iTop, iBot];
15182         if (!iRight && 0 !== iRight) {
15183             this.padding = [iTop, iTop, iTop, iTop];
15184         } else if (!iBot && 0 !== iBot) {
15185             this.padding = [iTop, iRight, iTop, iRight];
15186         } else {
15187             this.padding = [iTop, iRight, iBot, iLeft];
15188         }
15189     },
15190
15191     /**
15192      * Stores the initial placement of the linked element.
15193      * @method setInitialPosition
15194      * @param {int} diffX   the X offset, default 0
15195      * @param {int} diffY   the Y offset, default 0
15196      */
15197     setInitPosition: function(diffX, diffY) {
15198         var el = this.getEl();
15199
15200         if (!this.DDM.verifyEl(el)) {
15201             return;
15202         }
15203
15204         var dx = diffX || 0;
15205         var dy = diffY || 0;
15206
15207         var p = Dom.getXY( el );
15208
15209         this.initPageX = p[0] - dx;
15210         this.initPageY = p[1] - dy;
15211
15212         this.lastPageX = p[0];
15213         this.lastPageY = p[1];
15214
15215
15216         this.setStartPosition(p);
15217     },
15218
15219     /**
15220      * Sets the start position of the element.  This is set when the obj
15221      * is initialized, the reset when a drag is started.
15222      * @method setStartPosition
15223      * @param pos current position (from previous lookup)
15224      * @private
15225      */
15226     setStartPosition: function(pos) {
15227         var p = pos || Dom.getXY( this.getEl() );
15228         this.deltaSetXY = null;
15229
15230         this.startPageX = p[0];
15231         this.startPageY = p[1];
15232     },
15233
15234     /**
15235      * Add this instance to a group of related drag/drop objects.  All
15236      * instances belong to at least one group, and can belong to as many
15237      * groups as needed.
15238      * @method addToGroup
15239      * @param sGroup {string} the name of the group
15240      */
15241     addToGroup: function(sGroup) {
15242         this.groups[sGroup] = true;
15243         this.DDM.regDragDrop(this, sGroup);
15244     },
15245
15246     /**
15247      * Remove's this instance from the supplied interaction group
15248      * @method removeFromGroup
15249      * @param {string}  sGroup  The group to drop
15250      */
15251     removeFromGroup: function(sGroup) {
15252         if (this.groups[sGroup]) {
15253             delete this.groups[sGroup];
15254         }
15255
15256         this.DDM.removeDDFromGroup(this, sGroup);
15257     },
15258
15259     /**
15260      * Allows you to specify that an element other than the linked element
15261      * will be moved with the cursor during a drag
15262      * @method setDragElId
15263      * @param id {string} the id of the element that will be used to initiate the drag
15264      */
15265     setDragElId: function(id) {
15266         this.dragElId = id;
15267     },
15268
15269     /**
15270      * Allows you to specify a child of the linked element that should be
15271      * used to initiate the drag operation.  An example of this would be if
15272      * you have a content div with text and links.  Clicking anywhere in the
15273      * content area would normally start the drag operation.  Use this method
15274      * to specify that an element inside of the content div is the element
15275      * that starts the drag operation.
15276      * @method setHandleElId
15277      * @param id {string} the id of the element that will be used to
15278      * initiate the drag.
15279      */
15280     setHandleElId: function(id) {
15281         if (typeof id !== "string") {
15282             id = Roo.id(id);
15283         }
15284         this.handleElId = id;
15285         this.DDM.regHandle(this.id, id);
15286     },
15287
15288     /**
15289      * Allows you to set an element outside of the linked element as a drag
15290      * handle
15291      * @method setOuterHandleElId
15292      * @param id the id of the element that will be used to initiate the drag
15293      */
15294     setOuterHandleElId: function(id) {
15295         if (typeof id !== "string") {
15296             id = Roo.id(id);
15297         }
15298         Event.on(id, "mousedown",
15299                 this.handleMouseDown, this);
15300         this.setHandleElId(id);
15301
15302         this.hasOuterHandles = true;
15303     },
15304
15305     /**
15306      * Remove all drag and drop hooks for this element
15307      * @method unreg
15308      */
15309     unreg: function() {
15310         Event.un(this.id, "mousedown",
15311                 this.handleMouseDown);
15312         this._domRef = null;
15313         this.DDM._remove(this);
15314     },
15315
15316     destroy : function(){
15317         this.unreg();
15318     },
15319
15320     /**
15321      * Returns true if this instance is locked, or the drag drop mgr is locked
15322      * (meaning that all drag/drop is disabled on the page.)
15323      * @method isLocked
15324      * @return {boolean} true if this obj or all drag/drop is locked, else
15325      * false
15326      */
15327     isLocked: function() {
15328         return (this.DDM.isLocked() || this.locked);
15329     },
15330
15331     /**
15332      * Fired when this object is clicked
15333      * @method handleMouseDown
15334      * @param {Event} e
15335      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15336      * @private
15337      */
15338     handleMouseDown: function(e, oDD){
15339         if (this.primaryButtonOnly && e.button != 0) {
15340             return;
15341         }
15342
15343         if (this.isLocked()) {
15344             return;
15345         }
15346
15347         this.DDM.refreshCache(this.groups);
15348
15349         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15350         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15351         } else {
15352             if (this.clickValidator(e)) {
15353
15354                 // set the initial element position
15355                 this.setStartPosition();
15356
15357
15358                 this.b4MouseDown(e);
15359                 this.onMouseDown(e);
15360
15361                 this.DDM.handleMouseDown(e, this);
15362
15363                 this.DDM.stopEvent(e);
15364             } else {
15365
15366
15367             }
15368         }
15369     },
15370
15371     clickValidator: function(e) {
15372         var target = e.getTarget();
15373         return ( this.isValidHandleChild(target) &&
15374                     (this.id == this.handleElId ||
15375                         this.DDM.handleWasClicked(target, this.id)) );
15376     },
15377
15378     /**
15379      * Allows you to specify a tag name that should not start a drag operation
15380      * when clicked.  This is designed to facilitate embedding links within a
15381      * drag handle that do something other than start the drag.
15382      * @method addInvalidHandleType
15383      * @param {string} tagName the type of element to exclude
15384      */
15385     addInvalidHandleType: function(tagName) {
15386         var type = tagName.toUpperCase();
15387         this.invalidHandleTypes[type] = type;
15388     },
15389
15390     /**
15391      * Lets you to specify an element id for a child of a drag handle
15392      * that should not initiate a drag
15393      * @method addInvalidHandleId
15394      * @param {string} id the element id of the element you wish to ignore
15395      */
15396     addInvalidHandleId: function(id) {
15397         if (typeof id !== "string") {
15398             id = Roo.id(id);
15399         }
15400         this.invalidHandleIds[id] = id;
15401     },
15402
15403     /**
15404      * Lets you specify a css class of elements that will not initiate a drag
15405      * @method addInvalidHandleClass
15406      * @param {string} cssClass the class of the elements you wish to ignore
15407      */
15408     addInvalidHandleClass: function(cssClass) {
15409         this.invalidHandleClasses.push(cssClass);
15410     },
15411
15412     /**
15413      * Unsets an excluded tag name set by addInvalidHandleType
15414      * @method removeInvalidHandleType
15415      * @param {string} tagName the type of element to unexclude
15416      */
15417     removeInvalidHandleType: function(tagName) {
15418         var type = tagName.toUpperCase();
15419         // this.invalidHandleTypes[type] = null;
15420         delete this.invalidHandleTypes[type];
15421     },
15422
15423     /**
15424      * Unsets an invalid handle id
15425      * @method removeInvalidHandleId
15426      * @param {string} id the id of the element to re-enable
15427      */
15428     removeInvalidHandleId: function(id) {
15429         if (typeof id !== "string") {
15430             id = Roo.id(id);
15431         }
15432         delete this.invalidHandleIds[id];
15433     },
15434
15435     /**
15436      * Unsets an invalid css class
15437      * @method removeInvalidHandleClass
15438      * @param {string} cssClass the class of the element(s) you wish to
15439      * re-enable
15440      */
15441     removeInvalidHandleClass: function(cssClass) {
15442         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15443             if (this.invalidHandleClasses[i] == cssClass) {
15444                 delete this.invalidHandleClasses[i];
15445             }
15446         }
15447     },
15448
15449     /**
15450      * Checks the tag exclusion list to see if this click should be ignored
15451      * @method isValidHandleChild
15452      * @param {HTMLElement} node the HTMLElement to evaluate
15453      * @return {boolean} true if this is a valid tag type, false if not
15454      */
15455     isValidHandleChild: function(node) {
15456
15457         var valid = true;
15458         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15459         var nodeName;
15460         try {
15461             nodeName = node.nodeName.toUpperCase();
15462         } catch(e) {
15463             nodeName = node.nodeName;
15464         }
15465         valid = valid && !this.invalidHandleTypes[nodeName];
15466         valid = valid && !this.invalidHandleIds[node.id];
15467
15468         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15469             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15470         }
15471
15472
15473         return valid;
15474
15475     },
15476
15477     /**
15478      * Create the array of horizontal tick marks if an interval was specified
15479      * in setXConstraint().
15480      * @method setXTicks
15481      * @private
15482      */
15483     setXTicks: function(iStartX, iTickSize) {
15484         this.xTicks = [];
15485         this.xTickSize = iTickSize;
15486
15487         var tickMap = {};
15488
15489         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15490             if (!tickMap[i]) {
15491                 this.xTicks[this.xTicks.length] = i;
15492                 tickMap[i] = true;
15493             }
15494         }
15495
15496         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15497             if (!tickMap[i]) {
15498                 this.xTicks[this.xTicks.length] = i;
15499                 tickMap[i] = true;
15500             }
15501         }
15502
15503         this.xTicks.sort(this.DDM.numericSort) ;
15504     },
15505
15506     /**
15507      * Create the array of vertical tick marks if an interval was specified in
15508      * setYConstraint().
15509      * @method setYTicks
15510      * @private
15511      */
15512     setYTicks: function(iStartY, iTickSize) {
15513         this.yTicks = [];
15514         this.yTickSize = iTickSize;
15515
15516         var tickMap = {};
15517
15518         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15519             if (!tickMap[i]) {
15520                 this.yTicks[this.yTicks.length] = i;
15521                 tickMap[i] = true;
15522             }
15523         }
15524
15525         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15526             if (!tickMap[i]) {
15527                 this.yTicks[this.yTicks.length] = i;
15528                 tickMap[i] = true;
15529             }
15530         }
15531
15532         this.yTicks.sort(this.DDM.numericSort) ;
15533     },
15534
15535     /**
15536      * By default, the element can be dragged any place on the screen.  Use
15537      * this method to limit the horizontal travel of the element.  Pass in
15538      * 0,0 for the parameters if you want to lock the drag to the y axis.
15539      * @method setXConstraint
15540      * @param {int} iLeft the number of pixels the element can move to the left
15541      * @param {int} iRight the number of pixels the element can move to the
15542      * right
15543      * @param {int} iTickSize optional parameter for specifying that the
15544      * element
15545      * should move iTickSize pixels at a time.
15546      */
15547     setXConstraint: function(iLeft, iRight, iTickSize) {
15548         this.leftConstraint = iLeft;
15549         this.rightConstraint = iRight;
15550
15551         this.minX = this.initPageX - iLeft;
15552         this.maxX = this.initPageX + iRight;
15553         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15554
15555         this.constrainX = true;
15556     },
15557
15558     /**
15559      * Clears any constraints applied to this instance.  Also clears ticks
15560      * since they can't exist independent of a constraint at this time.
15561      * @method clearConstraints
15562      */
15563     clearConstraints: function() {
15564         this.constrainX = false;
15565         this.constrainY = false;
15566         this.clearTicks();
15567     },
15568
15569     /**
15570      * Clears any tick interval defined for this instance
15571      * @method clearTicks
15572      */
15573     clearTicks: function() {
15574         this.xTicks = null;
15575         this.yTicks = null;
15576         this.xTickSize = 0;
15577         this.yTickSize = 0;
15578     },
15579
15580     /**
15581      * By default, the element can be dragged any place on the screen.  Set
15582      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15583      * parameters if you want to lock the drag to the x axis.
15584      * @method setYConstraint
15585      * @param {int} iUp the number of pixels the element can move up
15586      * @param {int} iDown the number of pixels the element can move down
15587      * @param {int} iTickSize optional parameter for specifying that the
15588      * element should move iTickSize pixels at a time.
15589      */
15590     setYConstraint: function(iUp, iDown, iTickSize) {
15591         this.topConstraint = iUp;
15592         this.bottomConstraint = iDown;
15593
15594         this.minY = this.initPageY - iUp;
15595         this.maxY = this.initPageY + iDown;
15596         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15597
15598         this.constrainY = true;
15599
15600     },
15601
15602     /**
15603      * resetConstraints must be called if you manually reposition a dd element.
15604      * @method resetConstraints
15605      * @param {boolean} maintainOffset
15606      */
15607     resetConstraints: function() {
15608
15609
15610         // Maintain offsets if necessary
15611         if (this.initPageX || this.initPageX === 0) {
15612             // figure out how much this thing has moved
15613             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15614             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15615
15616             this.setInitPosition(dx, dy);
15617
15618         // This is the first time we have detected the element's position
15619         } else {
15620             this.setInitPosition();
15621         }
15622
15623         if (this.constrainX) {
15624             this.setXConstraint( this.leftConstraint,
15625                                  this.rightConstraint,
15626                                  this.xTickSize        );
15627         }
15628
15629         if (this.constrainY) {
15630             this.setYConstraint( this.topConstraint,
15631                                  this.bottomConstraint,
15632                                  this.yTickSize         );
15633         }
15634     },
15635
15636     /**
15637      * Normally the drag element is moved pixel by pixel, but we can specify
15638      * that it move a number of pixels at a time.  This method resolves the
15639      * location when we have it set up like this.
15640      * @method getTick
15641      * @param {int} val where we want to place the object
15642      * @param {int[]} tickArray sorted array of valid points
15643      * @return {int} the closest tick
15644      * @private
15645      */
15646     getTick: function(val, tickArray) {
15647
15648         if (!tickArray) {
15649             // If tick interval is not defined, it is effectively 1 pixel,
15650             // so we return the value passed to us.
15651             return val;
15652         } else if (tickArray[0] >= val) {
15653             // The value is lower than the first tick, so we return the first
15654             // tick.
15655             return tickArray[0];
15656         } else {
15657             for (var i=0, len=tickArray.length; i<len; ++i) {
15658                 var next = i + 1;
15659                 if (tickArray[next] && tickArray[next] >= val) {
15660                     var diff1 = val - tickArray[i];
15661                     var diff2 = tickArray[next] - val;
15662                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15663                 }
15664             }
15665
15666             // The value is larger than the last tick, so we return the last
15667             // tick.
15668             return tickArray[tickArray.length - 1];
15669         }
15670     },
15671
15672     /**
15673      * toString method
15674      * @method toString
15675      * @return {string} string representation of the dd obj
15676      */
15677     toString: function() {
15678         return ("DragDrop " + this.id);
15679     }
15680
15681 });
15682
15683 })();
15684 /*
15685  * Based on:
15686  * Ext JS Library 1.1.1
15687  * Copyright(c) 2006-2007, Ext JS, LLC.
15688  *
15689  * Originally Released Under LGPL - original licence link has changed is not relivant.
15690  *
15691  * Fork - LGPL
15692  * <script type="text/javascript">
15693  */
15694
15695
15696 /**
15697  * The drag and drop utility provides a framework for building drag and drop
15698  * applications.  In addition to enabling drag and drop for specific elements,
15699  * the drag and drop elements are tracked by the manager class, and the
15700  * interactions between the various elements are tracked during the drag and
15701  * the implementing code is notified about these important moments.
15702  */
15703
15704 // Only load the library once.  Rewriting the manager class would orphan
15705 // existing drag and drop instances.
15706 if (!Roo.dd.DragDropMgr) {
15707
15708 /**
15709  * @class Roo.dd.DragDropMgr
15710  * DragDropMgr is a singleton that tracks the element interaction for
15711  * all DragDrop items in the window.  Generally, you will not call
15712  * this class directly, but it does have helper methods that could
15713  * be useful in your DragDrop implementations.
15714  * @singleton
15715  */
15716 Roo.dd.DragDropMgr = function() {
15717
15718     var Event = Roo.EventManager;
15719
15720     return {
15721
15722         /**
15723          * Two dimensional Array of registered DragDrop objects.  The first
15724          * dimension is the DragDrop item group, the second the DragDrop
15725          * object.
15726          * @property ids
15727          * @type {string: string}
15728          * @private
15729          * @static
15730          */
15731         ids: {},
15732
15733         /**
15734          * Array of element ids defined as drag handles.  Used to determine
15735          * if the element that generated the mousedown event is actually the
15736          * handle and not the html element itself.
15737          * @property handleIds
15738          * @type {string: string}
15739          * @private
15740          * @static
15741          */
15742         handleIds: {},
15743
15744         /**
15745          * the DragDrop object that is currently being dragged
15746          * @property dragCurrent
15747          * @type DragDrop
15748          * @private
15749          * @static
15750          **/
15751         dragCurrent: null,
15752
15753         /**
15754          * the DragDrop object(s) that are being hovered over
15755          * @property dragOvers
15756          * @type Array
15757          * @private
15758          * @static
15759          */
15760         dragOvers: {},
15761
15762         /**
15763          * the X distance between the cursor and the object being dragged
15764          * @property deltaX
15765          * @type int
15766          * @private
15767          * @static
15768          */
15769         deltaX: 0,
15770
15771         /**
15772          * the Y distance between the cursor and the object being dragged
15773          * @property deltaY
15774          * @type int
15775          * @private
15776          * @static
15777          */
15778         deltaY: 0,
15779
15780         /**
15781          * Flag to determine if we should prevent the default behavior of the
15782          * events we define. By default this is true, but this can be set to
15783          * false if you need the default behavior (not recommended)
15784          * @property preventDefault
15785          * @type boolean
15786          * @static
15787          */
15788         preventDefault: true,
15789
15790         /**
15791          * Flag to determine if we should stop the propagation of the events
15792          * we generate. This is true by default but you may want to set it to
15793          * false if the html element contains other features that require the
15794          * mouse click.
15795          * @property stopPropagation
15796          * @type boolean
15797          * @static
15798          */
15799         stopPropagation: true,
15800
15801         /**
15802          * Internal flag that is set to true when drag and drop has been
15803          * intialized
15804          * @property initialized
15805          * @private
15806          * @static
15807          */
15808         initalized: false,
15809
15810         /**
15811          * All drag and drop can be disabled.
15812          * @property locked
15813          * @private
15814          * @static
15815          */
15816         locked: false,
15817
15818         /**
15819          * Called the first time an element is registered.
15820          * @method init
15821          * @private
15822          * @static
15823          */
15824         init: function() {
15825             this.initialized = true;
15826         },
15827
15828         /**
15829          * In point mode, drag and drop interaction is defined by the
15830          * location of the cursor during the drag/drop
15831          * @property POINT
15832          * @type int
15833          * @static
15834          */
15835         POINT: 0,
15836
15837         /**
15838          * In intersect mode, drag and drop interactio nis defined by the
15839          * overlap of two or more drag and drop objects.
15840          * @property INTERSECT
15841          * @type int
15842          * @static
15843          */
15844         INTERSECT: 1,
15845
15846         /**
15847          * The current drag and drop mode.  Default: POINT
15848          * @property mode
15849          * @type int
15850          * @static
15851          */
15852         mode: 0,
15853
15854         /**
15855          * Runs method on all drag and drop objects
15856          * @method _execOnAll
15857          * @private
15858          * @static
15859          */
15860         _execOnAll: function(sMethod, args) {
15861             for (var i in this.ids) {
15862                 for (var j in this.ids[i]) {
15863                     var oDD = this.ids[i][j];
15864                     if (! this.isTypeOfDD(oDD)) {
15865                         continue;
15866                     }
15867                     oDD[sMethod].apply(oDD, args);
15868                 }
15869             }
15870         },
15871
15872         /**
15873          * Drag and drop initialization.  Sets up the global event handlers
15874          * @method _onLoad
15875          * @private
15876          * @static
15877          */
15878         _onLoad: function() {
15879
15880             this.init();
15881
15882
15883             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15884             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15885             Event.on(window,   "unload",    this._onUnload, this, true);
15886             Event.on(window,   "resize",    this._onResize, this, true);
15887             // Event.on(window,   "mouseout",    this._test);
15888
15889         },
15890
15891         /**
15892          * Reset constraints on all drag and drop objs
15893          * @method _onResize
15894          * @private
15895          * @static
15896          */
15897         _onResize: function(e) {
15898             this._execOnAll("resetConstraints", []);
15899         },
15900
15901         /**
15902          * Lock all drag and drop functionality
15903          * @method lock
15904          * @static
15905          */
15906         lock: function() { this.locked = true; },
15907
15908         /**
15909          * Unlock all drag and drop functionality
15910          * @method unlock
15911          * @static
15912          */
15913         unlock: function() { this.locked = false; },
15914
15915         /**
15916          * Is drag and drop locked?
15917          * @method isLocked
15918          * @return {boolean} True if drag and drop is locked, false otherwise.
15919          * @static
15920          */
15921         isLocked: function() { return this.locked; },
15922
15923         /**
15924          * Location cache that is set for all drag drop objects when a drag is
15925          * initiated, cleared when the drag is finished.
15926          * @property locationCache
15927          * @private
15928          * @static
15929          */
15930         locationCache: {},
15931
15932         /**
15933          * Set useCache to false if you want to force object the lookup of each
15934          * drag and drop linked element constantly during a drag.
15935          * @property useCache
15936          * @type boolean
15937          * @static
15938          */
15939         useCache: true,
15940
15941         /**
15942          * The number of pixels that the mouse needs to move after the
15943          * mousedown before the drag is initiated.  Default=3;
15944          * @property clickPixelThresh
15945          * @type int
15946          * @static
15947          */
15948         clickPixelThresh: 3,
15949
15950         /**
15951          * The number of milliseconds after the mousedown event to initiate the
15952          * drag if we don't get a mouseup event. Default=1000
15953          * @property clickTimeThresh
15954          * @type int
15955          * @static
15956          */
15957         clickTimeThresh: 350,
15958
15959         /**
15960          * Flag that indicates that either the drag pixel threshold or the
15961          * mousdown time threshold has been met
15962          * @property dragThreshMet
15963          * @type boolean
15964          * @private
15965          * @static
15966          */
15967         dragThreshMet: false,
15968
15969         /**
15970          * Timeout used for the click time threshold
15971          * @property clickTimeout
15972          * @type Object
15973          * @private
15974          * @static
15975          */
15976         clickTimeout: null,
15977
15978         /**
15979          * The X position of the mousedown event stored for later use when a
15980          * drag threshold is met.
15981          * @property startX
15982          * @type int
15983          * @private
15984          * @static
15985          */
15986         startX: 0,
15987
15988         /**
15989          * The Y position of the mousedown event stored for later use when a
15990          * drag threshold is met.
15991          * @property startY
15992          * @type int
15993          * @private
15994          * @static
15995          */
15996         startY: 0,
15997
15998         /**
15999          * Each DragDrop instance must be registered with the DragDropMgr.
16000          * This is executed in DragDrop.init()
16001          * @method regDragDrop
16002          * @param {DragDrop} oDD the DragDrop object to register
16003          * @param {String} sGroup the name of the group this element belongs to
16004          * @static
16005          */
16006         regDragDrop: function(oDD, sGroup) {
16007             if (!this.initialized) { this.init(); }
16008
16009             if (!this.ids[sGroup]) {
16010                 this.ids[sGroup] = {};
16011             }
16012             this.ids[sGroup][oDD.id] = oDD;
16013         },
16014
16015         /**
16016          * Removes the supplied dd instance from the supplied group. Executed
16017          * by DragDrop.removeFromGroup, so don't call this function directly.
16018          * @method removeDDFromGroup
16019          * @private
16020          * @static
16021          */
16022         removeDDFromGroup: function(oDD, sGroup) {
16023             if (!this.ids[sGroup]) {
16024                 this.ids[sGroup] = {};
16025             }
16026
16027             var obj = this.ids[sGroup];
16028             if (obj && obj[oDD.id]) {
16029                 delete obj[oDD.id];
16030             }
16031         },
16032
16033         /**
16034          * Unregisters a drag and drop item.  This is executed in
16035          * DragDrop.unreg, use that method instead of calling this directly.
16036          * @method _remove
16037          * @private
16038          * @static
16039          */
16040         _remove: function(oDD) {
16041             for (var g in oDD.groups) {
16042                 if (g && this.ids[g][oDD.id]) {
16043                     delete this.ids[g][oDD.id];
16044                 }
16045             }
16046             delete this.handleIds[oDD.id];
16047         },
16048
16049         /**
16050          * Each DragDrop handle element must be registered.  This is done
16051          * automatically when executing DragDrop.setHandleElId()
16052          * @method regHandle
16053          * @param {String} sDDId the DragDrop id this element is a handle for
16054          * @param {String} sHandleId the id of the element that is the drag
16055          * handle
16056          * @static
16057          */
16058         regHandle: function(sDDId, sHandleId) {
16059             if (!this.handleIds[sDDId]) {
16060                 this.handleIds[sDDId] = {};
16061             }
16062             this.handleIds[sDDId][sHandleId] = sHandleId;
16063         },
16064
16065         /**
16066          * Utility function to determine if a given element has been
16067          * registered as a drag drop item.
16068          * @method isDragDrop
16069          * @param {String} id the element id to check
16070          * @return {boolean} true if this element is a DragDrop item,
16071          * false otherwise
16072          * @static
16073          */
16074         isDragDrop: function(id) {
16075             return ( this.getDDById(id) ) ? true : false;
16076         },
16077
16078         /**
16079          * Returns the drag and drop instances that are in all groups the
16080          * passed in instance belongs to.
16081          * @method getRelated
16082          * @param {DragDrop} p_oDD the obj to get related data for
16083          * @param {boolean} bTargetsOnly if true, only return targetable objs
16084          * @return {DragDrop[]} the related instances
16085          * @static
16086          */
16087         getRelated: function(p_oDD, bTargetsOnly) {
16088             var oDDs = [];
16089             for (var i in p_oDD.groups) {
16090                 for (j in this.ids[i]) {
16091                     var dd = this.ids[i][j];
16092                     if (! this.isTypeOfDD(dd)) {
16093                         continue;
16094                     }
16095                     if (!bTargetsOnly || dd.isTarget) {
16096                         oDDs[oDDs.length] = dd;
16097                     }
16098                 }
16099             }
16100
16101             return oDDs;
16102         },
16103
16104         /**
16105          * Returns true if the specified dd target is a legal target for
16106          * the specifice drag obj
16107          * @method isLegalTarget
16108          * @param {DragDrop} the drag obj
16109          * @param {DragDrop} the target
16110          * @return {boolean} true if the target is a legal target for the
16111          * dd obj
16112          * @static
16113          */
16114         isLegalTarget: function (oDD, oTargetDD) {
16115             var targets = this.getRelated(oDD, true);
16116             for (var i=0, len=targets.length;i<len;++i) {
16117                 if (targets[i].id == oTargetDD.id) {
16118                     return true;
16119                 }
16120             }
16121
16122             return false;
16123         },
16124
16125         /**
16126          * My goal is to be able to transparently determine if an object is
16127          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16128          * returns "object", oDD.constructor.toString() always returns
16129          * "DragDrop" and not the name of the subclass.  So for now it just
16130          * evaluates a well-known variable in DragDrop.
16131          * @method isTypeOfDD
16132          * @param {Object} the object to evaluate
16133          * @return {boolean} true if typeof oDD = DragDrop
16134          * @static
16135          */
16136         isTypeOfDD: function (oDD) {
16137             return (oDD && oDD.__ygDragDrop);
16138         },
16139
16140         /**
16141          * Utility function to determine if a given element has been
16142          * registered as a drag drop handle for the given Drag Drop object.
16143          * @method isHandle
16144          * @param {String} id the element id to check
16145          * @return {boolean} true if this element is a DragDrop handle, false
16146          * otherwise
16147          * @static
16148          */
16149         isHandle: function(sDDId, sHandleId) {
16150             return ( this.handleIds[sDDId] &&
16151                             this.handleIds[sDDId][sHandleId] );
16152         },
16153
16154         /**
16155          * Returns the DragDrop instance for a given id
16156          * @method getDDById
16157          * @param {String} id the id of the DragDrop object
16158          * @return {DragDrop} the drag drop object, null if it is not found
16159          * @static
16160          */
16161         getDDById: function(id) {
16162             for (var i in this.ids) {
16163                 if (this.ids[i][id]) {
16164                     return this.ids[i][id];
16165                 }
16166             }
16167             return null;
16168         },
16169
16170         /**
16171          * Fired after a registered DragDrop object gets the mousedown event.
16172          * Sets up the events required to track the object being dragged
16173          * @method handleMouseDown
16174          * @param {Event} e the event
16175          * @param oDD the DragDrop object being dragged
16176          * @private
16177          * @static
16178          */
16179         handleMouseDown: function(e, oDD) {
16180             if(Roo.QuickTips){
16181                 Roo.QuickTips.disable();
16182             }
16183             this.currentTarget = e.getTarget();
16184
16185             this.dragCurrent = oDD;
16186
16187             var el = oDD.getEl();
16188
16189             // track start position
16190             this.startX = e.getPageX();
16191             this.startY = e.getPageY();
16192
16193             this.deltaX = this.startX - el.offsetLeft;
16194             this.deltaY = this.startY - el.offsetTop;
16195
16196             this.dragThreshMet = false;
16197
16198             this.clickTimeout = setTimeout(
16199                     function() {
16200                         var DDM = Roo.dd.DDM;
16201                         DDM.startDrag(DDM.startX, DDM.startY);
16202                     },
16203                     this.clickTimeThresh );
16204         },
16205
16206         /**
16207          * Fired when either the drag pixel threshol or the mousedown hold
16208          * time threshold has been met.
16209          * @method startDrag
16210          * @param x {int} the X position of the original mousedown
16211          * @param y {int} the Y position of the original mousedown
16212          * @static
16213          */
16214         startDrag: function(x, y) {
16215             clearTimeout(this.clickTimeout);
16216             if (this.dragCurrent) {
16217                 this.dragCurrent.b4StartDrag(x, y);
16218                 this.dragCurrent.startDrag(x, y);
16219             }
16220             this.dragThreshMet = true;
16221         },
16222
16223         /**
16224          * Internal function to handle the mouseup event.  Will be invoked
16225          * from the context of the document.
16226          * @method handleMouseUp
16227          * @param {Event} e the event
16228          * @private
16229          * @static
16230          */
16231         handleMouseUp: function(e) {
16232
16233             if(Roo.QuickTips){
16234                 Roo.QuickTips.enable();
16235             }
16236             if (! this.dragCurrent) {
16237                 return;
16238             }
16239
16240             clearTimeout(this.clickTimeout);
16241
16242             if (this.dragThreshMet) {
16243                 this.fireEvents(e, true);
16244             } else {
16245             }
16246
16247             this.stopDrag(e);
16248
16249             this.stopEvent(e);
16250         },
16251
16252         /**
16253          * Utility to stop event propagation and event default, if these
16254          * features are turned on.
16255          * @method stopEvent
16256          * @param {Event} e the event as returned by this.getEvent()
16257          * @static
16258          */
16259         stopEvent: function(e){
16260             if(this.stopPropagation) {
16261                 e.stopPropagation();
16262             }
16263
16264             if (this.preventDefault) {
16265                 e.preventDefault();
16266             }
16267         },
16268
16269         /**
16270          * Internal function to clean up event handlers after the drag
16271          * operation is complete
16272          * @method stopDrag
16273          * @param {Event} e the event
16274          * @private
16275          * @static
16276          */
16277         stopDrag: function(e) {
16278             // Fire the drag end event for the item that was dragged
16279             if (this.dragCurrent) {
16280                 if (this.dragThreshMet) {
16281                     this.dragCurrent.b4EndDrag(e);
16282                     this.dragCurrent.endDrag(e);
16283                 }
16284
16285                 this.dragCurrent.onMouseUp(e);
16286             }
16287
16288             this.dragCurrent = null;
16289             this.dragOvers = {};
16290         },
16291
16292         /**
16293          * Internal function to handle the mousemove event.  Will be invoked
16294          * from the context of the html element.
16295          *
16296          * @TODO figure out what we can do about mouse events lost when the
16297          * user drags objects beyond the window boundary.  Currently we can
16298          * detect this in internet explorer by verifying that the mouse is
16299          * down during the mousemove event.  Firefox doesn't give us the
16300          * button state on the mousemove event.
16301          * @method handleMouseMove
16302          * @param {Event} e the event
16303          * @private
16304          * @static
16305          */
16306         handleMouseMove: function(e) {
16307             if (! this.dragCurrent) {
16308                 return true;
16309             }
16310
16311             // var button = e.which || e.button;
16312
16313             // check for IE mouseup outside of page boundary
16314             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16315                 this.stopEvent(e);
16316                 return this.handleMouseUp(e);
16317             }
16318
16319             if (!this.dragThreshMet) {
16320                 var diffX = Math.abs(this.startX - e.getPageX());
16321                 var diffY = Math.abs(this.startY - e.getPageY());
16322                 if (diffX > this.clickPixelThresh ||
16323                             diffY > this.clickPixelThresh) {
16324                     this.startDrag(this.startX, this.startY);
16325                 }
16326             }
16327
16328             if (this.dragThreshMet) {
16329                 this.dragCurrent.b4Drag(e);
16330                 this.dragCurrent.onDrag(e);
16331                 if(!this.dragCurrent.moveOnly){
16332                     this.fireEvents(e, false);
16333                 }
16334             }
16335
16336             this.stopEvent(e);
16337
16338             return true;
16339         },
16340
16341         /**
16342          * Iterates over all of the DragDrop elements to find ones we are
16343          * hovering over or dropping on
16344          * @method fireEvents
16345          * @param {Event} e the event
16346          * @param {boolean} isDrop is this a drop op or a mouseover op?
16347          * @private
16348          * @static
16349          */
16350         fireEvents: function(e, isDrop) {
16351             var dc = this.dragCurrent;
16352
16353             // If the user did the mouse up outside of the window, we could
16354             // get here even though we have ended the drag.
16355             if (!dc || dc.isLocked()) {
16356                 return;
16357             }
16358
16359             var pt = e.getPoint();
16360
16361             // cache the previous dragOver array
16362             var oldOvers = [];
16363
16364             var outEvts   = [];
16365             var overEvts  = [];
16366             var dropEvts  = [];
16367             var enterEvts = [];
16368
16369             // Check to see if the object(s) we were hovering over is no longer
16370             // being hovered over so we can fire the onDragOut event
16371             for (var i in this.dragOvers) {
16372
16373                 var ddo = this.dragOvers[i];
16374
16375                 if (! this.isTypeOfDD(ddo)) {
16376                     continue;
16377                 }
16378
16379                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16380                     outEvts.push( ddo );
16381                 }
16382
16383                 oldOvers[i] = true;
16384                 delete this.dragOvers[i];
16385             }
16386
16387             for (var sGroup in dc.groups) {
16388
16389                 if ("string" != typeof sGroup) {
16390                     continue;
16391                 }
16392
16393                 for (i in this.ids[sGroup]) {
16394                     var oDD = this.ids[sGroup][i];
16395                     if (! this.isTypeOfDD(oDD)) {
16396                         continue;
16397                     }
16398
16399                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16400                         if (this.isOverTarget(pt, oDD, this.mode)) {
16401                             // look for drop interactions
16402                             if (isDrop) {
16403                                 dropEvts.push( oDD );
16404                             // look for drag enter and drag over interactions
16405                             } else {
16406
16407                                 // initial drag over: dragEnter fires
16408                                 if (!oldOvers[oDD.id]) {
16409                                     enterEvts.push( oDD );
16410                                 // subsequent drag overs: dragOver fires
16411                                 } else {
16412                                     overEvts.push( oDD );
16413                                 }
16414
16415                                 this.dragOvers[oDD.id] = oDD;
16416                             }
16417                         }
16418                     }
16419                 }
16420             }
16421
16422             if (this.mode) {
16423                 if (outEvts.length) {
16424                     dc.b4DragOut(e, outEvts);
16425                     dc.onDragOut(e, outEvts);
16426                 }
16427
16428                 if (enterEvts.length) {
16429                     dc.onDragEnter(e, enterEvts);
16430                 }
16431
16432                 if (overEvts.length) {
16433                     dc.b4DragOver(e, overEvts);
16434                     dc.onDragOver(e, overEvts);
16435                 }
16436
16437                 if (dropEvts.length) {
16438                     dc.b4DragDrop(e, dropEvts);
16439                     dc.onDragDrop(e, dropEvts);
16440                 }
16441
16442             } else {
16443                 // fire dragout events
16444                 var len = 0;
16445                 for (i=0, len=outEvts.length; i<len; ++i) {
16446                     dc.b4DragOut(e, outEvts[i].id);
16447                     dc.onDragOut(e, outEvts[i].id);
16448                 }
16449
16450                 // fire enter events
16451                 for (i=0,len=enterEvts.length; i<len; ++i) {
16452                     // dc.b4DragEnter(e, oDD.id);
16453                     dc.onDragEnter(e, enterEvts[i].id);
16454                 }
16455
16456                 // fire over events
16457                 for (i=0,len=overEvts.length; i<len; ++i) {
16458                     dc.b4DragOver(e, overEvts[i].id);
16459                     dc.onDragOver(e, overEvts[i].id);
16460                 }
16461
16462                 // fire drop events
16463                 for (i=0, len=dropEvts.length; i<len; ++i) {
16464                     dc.b4DragDrop(e, dropEvts[i].id);
16465                     dc.onDragDrop(e, dropEvts[i].id);
16466                 }
16467
16468             }
16469
16470             // notify about a drop that did not find a target
16471             if (isDrop && !dropEvts.length) {
16472                 dc.onInvalidDrop(e);
16473             }
16474
16475         },
16476
16477         /**
16478          * Helper function for getting the best match from the list of drag
16479          * and drop objects returned by the drag and drop events when we are
16480          * in INTERSECT mode.  It returns either the first object that the
16481          * cursor is over, or the object that has the greatest overlap with
16482          * the dragged element.
16483          * @method getBestMatch
16484          * @param  {DragDrop[]} dds The array of drag and drop objects
16485          * targeted
16486          * @return {DragDrop}       The best single match
16487          * @static
16488          */
16489         getBestMatch: function(dds) {
16490             var winner = null;
16491             // Return null if the input is not what we expect
16492             //if (!dds || !dds.length || dds.length == 0) {
16493                // winner = null;
16494             // If there is only one item, it wins
16495             //} else if (dds.length == 1) {
16496
16497             var len = dds.length;
16498
16499             if (len == 1) {
16500                 winner = dds[0];
16501             } else {
16502                 // Loop through the targeted items
16503                 for (var i=0; i<len; ++i) {
16504                     var dd = dds[i];
16505                     // If the cursor is over the object, it wins.  If the
16506                     // cursor is over multiple matches, the first one we come
16507                     // to wins.
16508                     if (dd.cursorIsOver) {
16509                         winner = dd;
16510                         break;
16511                     // Otherwise the object with the most overlap wins
16512                     } else {
16513                         if (!winner ||
16514                             winner.overlap.getArea() < dd.overlap.getArea()) {
16515                             winner = dd;
16516                         }
16517                     }
16518                 }
16519             }
16520
16521             return winner;
16522         },
16523
16524         /**
16525          * Refreshes the cache of the top-left and bottom-right points of the
16526          * drag and drop objects in the specified group(s).  This is in the
16527          * format that is stored in the drag and drop instance, so typical
16528          * usage is:
16529          * <code>
16530          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16531          * </code>
16532          * Alternatively:
16533          * <code>
16534          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16535          * </code>
16536          * @TODO this really should be an indexed array.  Alternatively this
16537          * method could accept both.
16538          * @method refreshCache
16539          * @param {Object} groups an associative array of groups to refresh
16540          * @static
16541          */
16542         refreshCache: function(groups) {
16543             for (var sGroup in groups) {
16544                 if ("string" != typeof sGroup) {
16545                     continue;
16546                 }
16547                 for (var i in this.ids[sGroup]) {
16548                     var oDD = this.ids[sGroup][i];
16549
16550                     if (this.isTypeOfDD(oDD)) {
16551                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16552                         var loc = this.getLocation(oDD);
16553                         if (loc) {
16554                             this.locationCache[oDD.id] = loc;
16555                         } else {
16556                             delete this.locationCache[oDD.id];
16557                             // this will unregister the drag and drop object if
16558                             // the element is not in a usable state
16559                             // oDD.unreg();
16560                         }
16561                     }
16562                 }
16563             }
16564         },
16565
16566         /**
16567          * This checks to make sure an element exists and is in the DOM.  The
16568          * main purpose is to handle cases where innerHTML is used to remove
16569          * drag and drop objects from the DOM.  IE provides an 'unspecified
16570          * error' when trying to access the offsetParent of such an element
16571          * @method verifyEl
16572          * @param {HTMLElement} el the element to check
16573          * @return {boolean} true if the element looks usable
16574          * @static
16575          */
16576         verifyEl: function(el) {
16577             if (el) {
16578                 var parent;
16579                 if(Roo.isIE){
16580                     try{
16581                         parent = el.offsetParent;
16582                     }catch(e){}
16583                 }else{
16584                     parent = el.offsetParent;
16585                 }
16586                 if (parent) {
16587                     return true;
16588                 }
16589             }
16590
16591             return false;
16592         },
16593
16594         /**
16595          * Returns a Region object containing the drag and drop element's position
16596          * and size, including the padding configured for it
16597          * @method getLocation
16598          * @param {DragDrop} oDD the drag and drop object to get the
16599          *                       location for
16600          * @return {Roo.lib.Region} a Region object representing the total area
16601          *                             the element occupies, including any padding
16602          *                             the instance is configured for.
16603          * @static
16604          */
16605         getLocation: function(oDD) {
16606             if (! this.isTypeOfDD(oDD)) {
16607                 return null;
16608             }
16609
16610             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16611
16612             try {
16613                 pos= Roo.lib.Dom.getXY(el);
16614             } catch (e) { }
16615
16616             if (!pos) {
16617                 return null;
16618             }
16619
16620             x1 = pos[0];
16621             x2 = x1 + el.offsetWidth;
16622             y1 = pos[1];
16623             y2 = y1 + el.offsetHeight;
16624
16625             t = y1 - oDD.padding[0];
16626             r = x2 + oDD.padding[1];
16627             b = y2 + oDD.padding[2];
16628             l = x1 - oDD.padding[3];
16629
16630             return new Roo.lib.Region( t, r, b, l );
16631         },
16632
16633         /**
16634          * Checks the cursor location to see if it over the target
16635          * @method isOverTarget
16636          * @param {Roo.lib.Point} pt The point to evaluate
16637          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16638          * @return {boolean} true if the mouse is over the target
16639          * @private
16640          * @static
16641          */
16642         isOverTarget: function(pt, oTarget, intersect) {
16643             // use cache if available
16644             var loc = this.locationCache[oTarget.id];
16645             if (!loc || !this.useCache) {
16646                 loc = this.getLocation(oTarget);
16647                 this.locationCache[oTarget.id] = loc;
16648
16649             }
16650
16651             if (!loc) {
16652                 return false;
16653             }
16654
16655             oTarget.cursorIsOver = loc.contains( pt );
16656
16657             // DragDrop is using this as a sanity check for the initial mousedown
16658             // in this case we are done.  In POINT mode, if the drag obj has no
16659             // contraints, we are also done. Otherwise we need to evaluate the
16660             // location of the target as related to the actual location of the
16661             // dragged element.
16662             var dc = this.dragCurrent;
16663             if (!dc || !dc.getTargetCoord ||
16664                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16665                 return oTarget.cursorIsOver;
16666             }
16667
16668             oTarget.overlap = null;
16669
16670             // Get the current location of the drag element, this is the
16671             // location of the mouse event less the delta that represents
16672             // where the original mousedown happened on the element.  We
16673             // need to consider constraints and ticks as well.
16674             var pos = dc.getTargetCoord(pt.x, pt.y);
16675
16676             var el = dc.getDragEl();
16677             var curRegion = new Roo.lib.Region( pos.y,
16678                                                    pos.x + el.offsetWidth,
16679                                                    pos.y + el.offsetHeight,
16680                                                    pos.x );
16681
16682             var overlap = curRegion.intersect(loc);
16683
16684             if (overlap) {
16685                 oTarget.overlap = overlap;
16686                 return (intersect) ? true : oTarget.cursorIsOver;
16687             } else {
16688                 return false;
16689             }
16690         },
16691
16692         /**
16693          * unload event handler
16694          * @method _onUnload
16695          * @private
16696          * @static
16697          */
16698         _onUnload: function(e, me) {
16699             Roo.dd.DragDropMgr.unregAll();
16700         },
16701
16702         /**
16703          * Cleans up the drag and drop events and objects.
16704          * @method unregAll
16705          * @private
16706          * @static
16707          */
16708         unregAll: function() {
16709
16710             if (this.dragCurrent) {
16711                 this.stopDrag();
16712                 this.dragCurrent = null;
16713             }
16714
16715             this._execOnAll("unreg", []);
16716
16717             for (i in this.elementCache) {
16718                 delete this.elementCache[i];
16719             }
16720
16721             this.elementCache = {};
16722             this.ids = {};
16723         },
16724
16725         /**
16726          * A cache of DOM elements
16727          * @property elementCache
16728          * @private
16729          * @static
16730          */
16731         elementCache: {},
16732
16733         /**
16734          * Get the wrapper for the DOM element specified
16735          * @method getElWrapper
16736          * @param {String} id the id of the element to get
16737          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16738          * @private
16739          * @deprecated This wrapper isn't that useful
16740          * @static
16741          */
16742         getElWrapper: function(id) {
16743             var oWrapper = this.elementCache[id];
16744             if (!oWrapper || !oWrapper.el) {
16745                 oWrapper = this.elementCache[id] =
16746                     new this.ElementWrapper(Roo.getDom(id));
16747             }
16748             return oWrapper;
16749         },
16750
16751         /**
16752          * Returns the actual DOM element
16753          * @method getElement
16754          * @param {String} id the id of the elment to get
16755          * @return {Object} The element
16756          * @deprecated use Roo.getDom instead
16757          * @static
16758          */
16759         getElement: function(id) {
16760             return Roo.getDom(id);
16761         },
16762
16763         /**
16764          * Returns the style property for the DOM element (i.e.,
16765          * document.getElById(id).style)
16766          * @method getCss
16767          * @param {String} id the id of the elment to get
16768          * @return {Object} The style property of the element
16769          * @deprecated use Roo.getDom instead
16770          * @static
16771          */
16772         getCss: function(id) {
16773             var el = Roo.getDom(id);
16774             return (el) ? el.style : null;
16775         },
16776
16777         /**
16778          * Inner class for cached elements
16779          * @class DragDropMgr.ElementWrapper
16780          * @for DragDropMgr
16781          * @private
16782          * @deprecated
16783          */
16784         ElementWrapper: function(el) {
16785                 /**
16786                  * The element
16787                  * @property el
16788                  */
16789                 this.el = el || null;
16790                 /**
16791                  * The element id
16792                  * @property id
16793                  */
16794                 this.id = this.el && el.id;
16795                 /**
16796                  * A reference to the style property
16797                  * @property css
16798                  */
16799                 this.css = this.el && el.style;
16800             },
16801
16802         /**
16803          * Returns the X position of an html element
16804          * @method getPosX
16805          * @param el the element for which to get the position
16806          * @return {int} the X coordinate
16807          * @for DragDropMgr
16808          * @deprecated use Roo.lib.Dom.getX instead
16809          * @static
16810          */
16811         getPosX: function(el) {
16812             return Roo.lib.Dom.getX(el);
16813         },
16814
16815         /**
16816          * Returns the Y position of an html element
16817          * @method getPosY
16818          * @param el the element for which to get the position
16819          * @return {int} the Y coordinate
16820          * @deprecated use Roo.lib.Dom.getY instead
16821          * @static
16822          */
16823         getPosY: function(el) {
16824             return Roo.lib.Dom.getY(el);
16825         },
16826
16827         /**
16828          * Swap two nodes.  In IE, we use the native method, for others we
16829          * emulate the IE behavior
16830          * @method swapNode
16831          * @param n1 the first node to swap
16832          * @param n2 the other node to swap
16833          * @static
16834          */
16835         swapNode: function(n1, n2) {
16836             if (n1.swapNode) {
16837                 n1.swapNode(n2);
16838             } else {
16839                 var p = n2.parentNode;
16840                 var s = n2.nextSibling;
16841
16842                 if (s == n1) {
16843                     p.insertBefore(n1, n2);
16844                 } else if (n2 == n1.nextSibling) {
16845                     p.insertBefore(n2, n1);
16846                 } else {
16847                     n1.parentNode.replaceChild(n2, n1);
16848                     p.insertBefore(n1, s);
16849                 }
16850             }
16851         },
16852
16853         /**
16854          * Returns the current scroll position
16855          * @method getScroll
16856          * @private
16857          * @static
16858          */
16859         getScroll: function () {
16860             var t, l, dde=document.documentElement, db=document.body;
16861             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16862                 t = dde.scrollTop;
16863                 l = dde.scrollLeft;
16864             } else if (db) {
16865                 t = db.scrollTop;
16866                 l = db.scrollLeft;
16867             } else {
16868
16869             }
16870             return { top: t, left: l };
16871         },
16872
16873         /**
16874          * Returns the specified element style property
16875          * @method getStyle
16876          * @param {HTMLElement} el          the element
16877          * @param {string}      styleProp   the style property
16878          * @return {string} The value of the style property
16879          * @deprecated use Roo.lib.Dom.getStyle
16880          * @static
16881          */
16882         getStyle: function(el, styleProp) {
16883             return Roo.fly(el).getStyle(styleProp);
16884         },
16885
16886         /**
16887          * Gets the scrollTop
16888          * @method getScrollTop
16889          * @return {int} the document's scrollTop
16890          * @static
16891          */
16892         getScrollTop: function () { return this.getScroll().top; },
16893
16894         /**
16895          * Gets the scrollLeft
16896          * @method getScrollLeft
16897          * @return {int} the document's scrollTop
16898          * @static
16899          */
16900         getScrollLeft: function () { return this.getScroll().left; },
16901
16902         /**
16903          * Sets the x/y position of an element to the location of the
16904          * target element.
16905          * @method moveToEl
16906          * @param {HTMLElement} moveEl      The element to move
16907          * @param {HTMLElement} targetEl    The position reference element
16908          * @static
16909          */
16910         moveToEl: function (moveEl, targetEl) {
16911             var aCoord = Roo.lib.Dom.getXY(targetEl);
16912             Roo.lib.Dom.setXY(moveEl, aCoord);
16913         },
16914
16915         /**
16916          * Numeric array sort function
16917          * @method numericSort
16918          * @static
16919          */
16920         numericSort: function(a, b) { return (a - b); },
16921
16922         /**
16923          * Internal counter
16924          * @property _timeoutCount
16925          * @private
16926          * @static
16927          */
16928         _timeoutCount: 0,
16929
16930         /**
16931          * Trying to make the load order less important.  Without this we get
16932          * an error if this file is loaded before the Event Utility.
16933          * @method _addListeners
16934          * @private
16935          * @static
16936          */
16937         _addListeners: function() {
16938             var DDM = Roo.dd.DDM;
16939             if ( Roo.lib.Event && document ) {
16940                 DDM._onLoad();
16941             } else {
16942                 if (DDM._timeoutCount > 2000) {
16943                 } else {
16944                     setTimeout(DDM._addListeners, 10);
16945                     if (document && document.body) {
16946                         DDM._timeoutCount += 1;
16947                     }
16948                 }
16949             }
16950         },
16951
16952         /**
16953          * Recursively searches the immediate parent and all child nodes for
16954          * the handle element in order to determine wheter or not it was
16955          * clicked.
16956          * @method handleWasClicked
16957          * @param node the html element to inspect
16958          * @static
16959          */
16960         handleWasClicked: function(node, id) {
16961             if (this.isHandle(id, node.id)) {
16962                 return true;
16963             } else {
16964                 // check to see if this is a text node child of the one we want
16965                 var p = node.parentNode;
16966
16967                 while (p) {
16968                     if (this.isHandle(id, p.id)) {
16969                         return true;
16970                     } else {
16971                         p = p.parentNode;
16972                     }
16973                 }
16974             }
16975
16976             return false;
16977         }
16978
16979     };
16980
16981 }();
16982
16983 // shorter alias, save a few bytes
16984 Roo.dd.DDM = Roo.dd.DragDropMgr;
16985 Roo.dd.DDM._addListeners();
16986
16987 }/*
16988  * Based on:
16989  * Ext JS Library 1.1.1
16990  * Copyright(c) 2006-2007, Ext JS, LLC.
16991  *
16992  * Originally Released Under LGPL - original licence link has changed is not relivant.
16993  *
16994  * Fork - LGPL
16995  * <script type="text/javascript">
16996  */
16997
16998 /**
16999  * @class Roo.dd.DD
17000  * A DragDrop implementation where the linked element follows the
17001  * mouse cursor during a drag.
17002  * @extends Roo.dd.DragDrop
17003  * @constructor
17004  * @param {String} id the id of the linked element
17005  * @param {String} sGroup the group of related DragDrop items
17006  * @param {object} config an object containing configurable attributes
17007  *                Valid properties for DD:
17008  *                    scroll
17009  */
17010 Roo.dd.DD = function(id, sGroup, config) {
17011     if (id) {
17012         this.init(id, sGroup, config);
17013     }
17014 };
17015
17016 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17017
17018     /**
17019      * When set to true, the utility automatically tries to scroll the browser
17020      * window wehn a drag and drop element is dragged near the viewport boundary.
17021      * Defaults to true.
17022      * @property scroll
17023      * @type boolean
17024      */
17025     scroll: true,
17026
17027     /**
17028      * Sets the pointer offset to the distance between the linked element's top
17029      * left corner and the location the element was clicked
17030      * @method autoOffset
17031      * @param {int} iPageX the X coordinate of the click
17032      * @param {int} iPageY the Y coordinate of the click
17033      */
17034     autoOffset: function(iPageX, iPageY) {
17035         var x = iPageX - this.startPageX;
17036         var y = iPageY - this.startPageY;
17037         this.setDelta(x, y);
17038     },
17039
17040     /**
17041      * Sets the pointer offset.  You can call this directly to force the
17042      * offset to be in a particular location (e.g., pass in 0,0 to set it
17043      * to the center of the object)
17044      * @method setDelta
17045      * @param {int} iDeltaX the distance from the left
17046      * @param {int} iDeltaY the distance from the top
17047      */
17048     setDelta: function(iDeltaX, iDeltaY) {
17049         this.deltaX = iDeltaX;
17050         this.deltaY = iDeltaY;
17051     },
17052
17053     /**
17054      * Sets the drag element to the location of the mousedown or click event,
17055      * maintaining the cursor location relative to the location on the element
17056      * that was clicked.  Override this if you want to place the element in a
17057      * location other than where the cursor is.
17058      * @method setDragElPos
17059      * @param {int} iPageX the X coordinate of the mousedown or drag event
17060      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17061      */
17062     setDragElPos: function(iPageX, iPageY) {
17063         // the first time we do this, we are going to check to make sure
17064         // the element has css positioning
17065
17066         var el = this.getDragEl();
17067         this.alignElWithMouse(el, iPageX, iPageY);
17068     },
17069
17070     /**
17071      * Sets the element to the location of the mousedown or click event,
17072      * maintaining the cursor location relative to the location on the element
17073      * that was clicked.  Override this if you want to place the element in a
17074      * location other than where the cursor is.
17075      * @method alignElWithMouse
17076      * @param {HTMLElement} el the element to move
17077      * @param {int} iPageX the X coordinate of the mousedown or drag event
17078      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17079      */
17080     alignElWithMouse: function(el, iPageX, iPageY) {
17081         var oCoord = this.getTargetCoord(iPageX, iPageY);
17082         var fly = el.dom ? el : Roo.fly(el);
17083         if (!this.deltaSetXY) {
17084             var aCoord = [oCoord.x, oCoord.y];
17085             fly.setXY(aCoord);
17086             var newLeft = fly.getLeft(true);
17087             var newTop  = fly.getTop(true);
17088             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17089         } else {
17090             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17091         }
17092
17093         this.cachePosition(oCoord.x, oCoord.y);
17094         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17095         return oCoord;
17096     },
17097
17098     /**
17099      * Saves the most recent position so that we can reset the constraints and
17100      * tick marks on-demand.  We need to know this so that we can calculate the
17101      * number of pixels the element is offset from its original position.
17102      * @method cachePosition
17103      * @param iPageX the current x position (optional, this just makes it so we
17104      * don't have to look it up again)
17105      * @param iPageY the current y position (optional, this just makes it so we
17106      * don't have to look it up again)
17107      */
17108     cachePosition: function(iPageX, iPageY) {
17109         if (iPageX) {
17110             this.lastPageX = iPageX;
17111             this.lastPageY = iPageY;
17112         } else {
17113             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17114             this.lastPageX = aCoord[0];
17115             this.lastPageY = aCoord[1];
17116         }
17117     },
17118
17119     /**
17120      * Auto-scroll the window if the dragged object has been moved beyond the
17121      * visible window boundary.
17122      * @method autoScroll
17123      * @param {int} x the drag element's x position
17124      * @param {int} y the drag element's y position
17125      * @param {int} h the height of the drag element
17126      * @param {int} w the width of the drag element
17127      * @private
17128      */
17129     autoScroll: function(x, y, h, w) {
17130
17131         if (this.scroll) {
17132             // The client height
17133             var clientH = Roo.lib.Dom.getViewWidth();
17134
17135             // The client width
17136             var clientW = Roo.lib.Dom.getViewHeight();
17137
17138             // The amt scrolled down
17139             var st = this.DDM.getScrollTop();
17140
17141             // The amt scrolled right
17142             var sl = this.DDM.getScrollLeft();
17143
17144             // Location of the bottom of the element
17145             var bot = h + y;
17146
17147             // Location of the right of the element
17148             var right = w + x;
17149
17150             // The distance from the cursor to the bottom of the visible area,
17151             // adjusted so that we don't scroll if the cursor is beyond the
17152             // element drag constraints
17153             var toBot = (clientH + st - y - this.deltaY);
17154
17155             // The distance from the cursor to the right of the visible area
17156             var toRight = (clientW + sl - x - this.deltaX);
17157
17158
17159             // How close to the edge the cursor must be before we scroll
17160             // var thresh = (document.all) ? 100 : 40;
17161             var thresh = 40;
17162
17163             // How many pixels to scroll per autoscroll op.  This helps to reduce
17164             // clunky scrolling. IE is more sensitive about this ... it needs this
17165             // value to be higher.
17166             var scrAmt = (document.all) ? 80 : 30;
17167
17168             // Scroll down if we are near the bottom of the visible page and the
17169             // obj extends below the crease
17170             if ( bot > clientH && toBot < thresh ) {
17171                 window.scrollTo(sl, st + scrAmt);
17172             }
17173
17174             // Scroll up if the window is scrolled down and the top of the object
17175             // goes above the top border
17176             if ( y < st && st > 0 && y - st < thresh ) {
17177                 window.scrollTo(sl, st - scrAmt);
17178             }
17179
17180             // Scroll right if the obj is beyond the right border and the cursor is
17181             // near the border.
17182             if ( right > clientW && toRight < thresh ) {
17183                 window.scrollTo(sl + scrAmt, st);
17184             }
17185
17186             // Scroll left if the window has been scrolled to the right and the obj
17187             // extends past the left border
17188             if ( x < sl && sl > 0 && x - sl < thresh ) {
17189                 window.scrollTo(sl - scrAmt, st);
17190             }
17191         }
17192     },
17193
17194     /**
17195      * Finds the location the element should be placed if we want to move
17196      * it to where the mouse location less the click offset would place us.
17197      * @method getTargetCoord
17198      * @param {int} iPageX the X coordinate of the click
17199      * @param {int} iPageY the Y coordinate of the click
17200      * @return an object that contains the coordinates (Object.x and Object.y)
17201      * @private
17202      */
17203     getTargetCoord: function(iPageX, iPageY) {
17204
17205
17206         var x = iPageX - this.deltaX;
17207         var y = iPageY - this.deltaY;
17208
17209         if (this.constrainX) {
17210             if (x < this.minX) { x = this.minX; }
17211             if (x > this.maxX) { x = this.maxX; }
17212         }
17213
17214         if (this.constrainY) {
17215             if (y < this.minY) { y = this.minY; }
17216             if (y > this.maxY) { y = this.maxY; }
17217         }
17218
17219         x = this.getTick(x, this.xTicks);
17220         y = this.getTick(y, this.yTicks);
17221
17222
17223         return {x:x, y:y};
17224     },
17225
17226     /*
17227      * Sets up config options specific to this class. Overrides
17228      * Roo.dd.DragDrop, but all versions of this method through the
17229      * inheritance chain are called
17230      */
17231     applyConfig: function() {
17232         Roo.dd.DD.superclass.applyConfig.call(this);
17233         this.scroll = (this.config.scroll !== false);
17234     },
17235
17236     /*
17237      * Event that fires prior to the onMouseDown event.  Overrides
17238      * Roo.dd.DragDrop.
17239      */
17240     b4MouseDown: function(e) {
17241         // this.resetConstraints();
17242         this.autoOffset(e.getPageX(),
17243                             e.getPageY());
17244     },
17245
17246     /*
17247      * Event that fires prior to the onDrag event.  Overrides
17248      * Roo.dd.DragDrop.
17249      */
17250     b4Drag: function(e) {
17251         this.setDragElPos(e.getPageX(),
17252                             e.getPageY());
17253     },
17254
17255     toString: function() {
17256         return ("DD " + this.id);
17257     }
17258
17259     //////////////////////////////////////////////////////////////////////////
17260     // Debugging ygDragDrop events that can be overridden
17261     //////////////////////////////////////////////////////////////////////////
17262     /*
17263     startDrag: function(x, y) {
17264     },
17265
17266     onDrag: function(e) {
17267     },
17268
17269     onDragEnter: function(e, id) {
17270     },
17271
17272     onDragOver: function(e, id) {
17273     },
17274
17275     onDragOut: function(e, id) {
17276     },
17277
17278     onDragDrop: function(e, id) {
17279     },
17280
17281     endDrag: function(e) {
17282     }
17283
17284     */
17285
17286 });/*
17287  * Based on:
17288  * Ext JS Library 1.1.1
17289  * Copyright(c) 2006-2007, Ext JS, LLC.
17290  *
17291  * Originally Released Under LGPL - original licence link has changed is not relivant.
17292  *
17293  * Fork - LGPL
17294  * <script type="text/javascript">
17295  */
17296
17297 /**
17298  * @class Roo.dd.DDProxy
17299  * A DragDrop implementation that inserts an empty, bordered div into
17300  * the document that follows the cursor during drag operations.  At the time of
17301  * the click, the frame div is resized to the dimensions of the linked html
17302  * element, and moved to the exact location of the linked element.
17303  *
17304  * References to the "frame" element refer to the single proxy element that
17305  * was created to be dragged in place of all DDProxy elements on the
17306  * page.
17307  *
17308  * @extends Roo.dd.DD
17309  * @constructor
17310  * @param {String} id the id of the linked html element
17311  * @param {String} sGroup the group of related DragDrop objects
17312  * @param {object} config an object containing configurable attributes
17313  *                Valid properties for DDProxy in addition to those in DragDrop:
17314  *                   resizeFrame, centerFrame, dragElId
17315  */
17316 Roo.dd.DDProxy = function(id, sGroup, config) {
17317     if (id) {
17318         this.init(id, sGroup, config);
17319         this.initFrame();
17320     }
17321 };
17322
17323 /**
17324  * The default drag frame div id
17325  * @property Roo.dd.DDProxy.dragElId
17326  * @type String
17327  * @static
17328  */
17329 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17330
17331 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17332
17333     /**
17334      * By default we resize the drag frame to be the same size as the element
17335      * we want to drag (this is to get the frame effect).  We can turn it off
17336      * if we want a different behavior.
17337      * @property resizeFrame
17338      * @type boolean
17339      */
17340     resizeFrame: true,
17341
17342     /**
17343      * By default the frame is positioned exactly where the drag element is, so
17344      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17345      * you do not have constraints on the obj is to have the drag frame centered
17346      * around the cursor.  Set centerFrame to true for this effect.
17347      * @property centerFrame
17348      * @type boolean
17349      */
17350     centerFrame: false,
17351
17352     /**
17353      * Creates the proxy element if it does not yet exist
17354      * @method createFrame
17355      */
17356     createFrame: function() {
17357         var self = this;
17358         var body = document.body;
17359
17360         if (!body || !body.firstChild) {
17361             setTimeout( function() { self.createFrame(); }, 50 );
17362             return;
17363         }
17364
17365         var div = this.getDragEl();
17366
17367         if (!div) {
17368             div    = document.createElement("div");
17369             div.id = this.dragElId;
17370             var s  = div.style;
17371
17372             s.position   = "absolute";
17373             s.visibility = "hidden";
17374             s.cursor     = "move";
17375             s.border     = "2px solid #aaa";
17376             s.zIndex     = 999;
17377
17378             // appendChild can blow up IE if invoked prior to the window load event
17379             // while rendering a table.  It is possible there are other scenarios
17380             // that would cause this to happen as well.
17381             body.insertBefore(div, body.firstChild);
17382         }
17383     },
17384
17385     /**
17386      * Initialization for the drag frame element.  Must be called in the
17387      * constructor of all subclasses
17388      * @method initFrame
17389      */
17390     initFrame: function() {
17391         this.createFrame();
17392     },
17393
17394     applyConfig: function() {
17395         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17396
17397         this.resizeFrame = (this.config.resizeFrame !== false);
17398         this.centerFrame = (this.config.centerFrame);
17399         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17400     },
17401
17402     /**
17403      * Resizes the drag frame to the dimensions of the clicked object, positions
17404      * it over the object, and finally displays it
17405      * @method showFrame
17406      * @param {int} iPageX X click position
17407      * @param {int} iPageY Y click position
17408      * @private
17409      */
17410     showFrame: function(iPageX, iPageY) {
17411         var el = this.getEl();
17412         var dragEl = this.getDragEl();
17413         var s = dragEl.style;
17414
17415         this._resizeProxy();
17416
17417         if (this.centerFrame) {
17418             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17419                            Math.round(parseInt(s.height, 10)/2) );
17420         }
17421
17422         this.setDragElPos(iPageX, iPageY);
17423
17424         Roo.fly(dragEl).show();
17425     },
17426
17427     /**
17428      * The proxy is automatically resized to the dimensions of the linked
17429      * element when a drag is initiated, unless resizeFrame is set to false
17430      * @method _resizeProxy
17431      * @private
17432      */
17433     _resizeProxy: function() {
17434         if (this.resizeFrame) {
17435             var el = this.getEl();
17436             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17437         }
17438     },
17439
17440     // overrides Roo.dd.DragDrop
17441     b4MouseDown: function(e) {
17442         var x = e.getPageX();
17443         var y = e.getPageY();
17444         this.autoOffset(x, y);
17445         this.setDragElPos(x, y);
17446     },
17447
17448     // overrides Roo.dd.DragDrop
17449     b4StartDrag: function(x, y) {
17450         // show the drag frame
17451         this.showFrame(x, y);
17452     },
17453
17454     // overrides Roo.dd.DragDrop
17455     b4EndDrag: function(e) {
17456         Roo.fly(this.getDragEl()).hide();
17457     },
17458
17459     // overrides Roo.dd.DragDrop
17460     // By default we try to move the element to the last location of the frame.
17461     // This is so that the default behavior mirrors that of Roo.dd.DD.
17462     endDrag: function(e) {
17463
17464         var lel = this.getEl();
17465         var del = this.getDragEl();
17466
17467         // Show the drag frame briefly so we can get its position
17468         del.style.visibility = "";
17469
17470         this.beforeMove();
17471         // Hide the linked element before the move to get around a Safari
17472         // rendering bug.
17473         lel.style.visibility = "hidden";
17474         Roo.dd.DDM.moveToEl(lel, del);
17475         del.style.visibility = "hidden";
17476         lel.style.visibility = "";
17477
17478         this.afterDrag();
17479     },
17480
17481     beforeMove : function(){
17482
17483     },
17484
17485     afterDrag : function(){
17486
17487     },
17488
17489     toString: function() {
17490         return ("DDProxy " + this.id);
17491     }
17492
17493 });
17494 /*
17495  * Based on:
17496  * Ext JS Library 1.1.1
17497  * Copyright(c) 2006-2007, Ext JS, LLC.
17498  *
17499  * Originally Released Under LGPL - original licence link has changed is not relivant.
17500  *
17501  * Fork - LGPL
17502  * <script type="text/javascript">
17503  */
17504
17505  /**
17506  * @class Roo.dd.DDTarget
17507  * A DragDrop implementation that does not move, but can be a drop
17508  * target.  You would get the same result by simply omitting implementation
17509  * for the event callbacks, but this way we reduce the processing cost of the
17510  * event listener and the callbacks.
17511  * @extends Roo.dd.DragDrop
17512  * @constructor
17513  * @param {String} id the id of the element that is a drop target
17514  * @param {String} sGroup the group of related DragDrop objects
17515  * @param {object} config an object containing configurable attributes
17516  *                 Valid properties for DDTarget in addition to those in
17517  *                 DragDrop:
17518  *                    none
17519  */
17520 Roo.dd.DDTarget = function(id, sGroup, config) {
17521     if (id) {
17522         this.initTarget(id, sGroup, config);
17523     }
17524     if (config.listeners || config.events) { 
17525        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17526             listeners : config.listeners || {}, 
17527             events : config.events || {} 
17528         });    
17529     }
17530 };
17531
17532 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17533 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17534     toString: function() {
17535         return ("DDTarget " + this.id);
17536     }
17537 });
17538 /*
17539  * Based on:
17540  * Ext JS Library 1.1.1
17541  * Copyright(c) 2006-2007, Ext JS, LLC.
17542  *
17543  * Originally Released Under LGPL - original licence link has changed is not relivant.
17544  *
17545  * Fork - LGPL
17546  * <script type="text/javascript">
17547  */
17548  
17549
17550 /**
17551  * @class Roo.dd.ScrollManager
17552  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17553  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17554  * @singleton
17555  */
17556 Roo.dd.ScrollManager = function(){
17557     var ddm = Roo.dd.DragDropMgr;
17558     var els = {};
17559     var dragEl = null;
17560     var proc = {};
17561     
17562     var onStop = function(e){
17563         dragEl = null;
17564         clearProc();
17565     };
17566     
17567     var triggerRefresh = function(){
17568         if(ddm.dragCurrent){
17569              ddm.refreshCache(ddm.dragCurrent.groups);
17570         }
17571     };
17572     
17573     var doScroll = function(){
17574         if(ddm.dragCurrent){
17575             var dds = Roo.dd.ScrollManager;
17576             if(!dds.animate){
17577                 if(proc.el.scroll(proc.dir, dds.increment)){
17578                     triggerRefresh();
17579                 }
17580             }else{
17581                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17582             }
17583         }
17584     };
17585     
17586     var clearProc = function(){
17587         if(proc.id){
17588             clearInterval(proc.id);
17589         }
17590         proc.id = 0;
17591         proc.el = null;
17592         proc.dir = "";
17593     };
17594     
17595     var startProc = function(el, dir){
17596         clearProc();
17597         proc.el = el;
17598         proc.dir = dir;
17599         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17600     };
17601     
17602     var onFire = function(e, isDrop){
17603         if(isDrop || !ddm.dragCurrent){ return; }
17604         var dds = Roo.dd.ScrollManager;
17605         if(!dragEl || dragEl != ddm.dragCurrent){
17606             dragEl = ddm.dragCurrent;
17607             // refresh regions on drag start
17608             dds.refreshCache();
17609         }
17610         
17611         var xy = Roo.lib.Event.getXY(e);
17612         var pt = new Roo.lib.Point(xy[0], xy[1]);
17613         for(var id in els){
17614             var el = els[id], r = el._region;
17615             if(r && r.contains(pt) && el.isScrollable()){
17616                 if(r.bottom - pt.y <= dds.thresh){
17617                     if(proc.el != el){
17618                         startProc(el, "down");
17619                     }
17620                     return;
17621                 }else if(r.right - pt.x <= dds.thresh){
17622                     if(proc.el != el){
17623                         startProc(el, "left");
17624                     }
17625                     return;
17626                 }else if(pt.y - r.top <= dds.thresh){
17627                     if(proc.el != el){
17628                         startProc(el, "up");
17629                     }
17630                     return;
17631                 }else if(pt.x - r.left <= dds.thresh){
17632                     if(proc.el != el){
17633                         startProc(el, "right");
17634                     }
17635                     return;
17636                 }
17637             }
17638         }
17639         clearProc();
17640     };
17641     
17642     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17643     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17644     
17645     return {
17646         /**
17647          * Registers new overflow element(s) to auto scroll
17648          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17649          */
17650         register : function(el){
17651             if(el instanceof Array){
17652                 for(var i = 0, len = el.length; i < len; i++) {
17653                         this.register(el[i]);
17654                 }
17655             }else{
17656                 el = Roo.get(el);
17657                 els[el.id] = el;
17658             }
17659         },
17660         
17661         /**
17662          * Unregisters overflow element(s) so they are no longer scrolled
17663          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17664          */
17665         unregister : function(el){
17666             if(el instanceof Array){
17667                 for(var i = 0, len = el.length; i < len; i++) {
17668                         this.unregister(el[i]);
17669                 }
17670             }else{
17671                 el = Roo.get(el);
17672                 delete els[el.id];
17673             }
17674         },
17675         
17676         /**
17677          * The number of pixels from the edge of a container the pointer needs to be to 
17678          * trigger scrolling (defaults to 25)
17679          * @type Number
17680          */
17681         thresh : 25,
17682         
17683         /**
17684          * The number of pixels to scroll in each scroll increment (defaults to 50)
17685          * @type Number
17686          */
17687         increment : 100,
17688         
17689         /**
17690          * The frequency of scrolls in milliseconds (defaults to 500)
17691          * @type Number
17692          */
17693         frequency : 500,
17694         
17695         /**
17696          * True to animate the scroll (defaults to true)
17697          * @type Boolean
17698          */
17699         animate: true,
17700         
17701         /**
17702          * The animation duration in seconds - 
17703          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17704          * @type Number
17705          */
17706         animDuration: .4,
17707         
17708         /**
17709          * Manually trigger a cache refresh.
17710          */
17711         refreshCache : function(){
17712             for(var id in els){
17713                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17714                     els[id]._region = els[id].getRegion();
17715                 }
17716             }
17717         }
17718     };
17719 }();/*
17720  * Based on:
17721  * Ext JS Library 1.1.1
17722  * Copyright(c) 2006-2007, Ext JS, LLC.
17723  *
17724  * Originally Released Under LGPL - original licence link has changed is not relivant.
17725  *
17726  * Fork - LGPL
17727  * <script type="text/javascript">
17728  */
17729  
17730
17731 /**
17732  * @class Roo.dd.Registry
17733  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17734  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17735  * @singleton
17736  */
17737 Roo.dd.Registry = function(){
17738     var elements = {}; 
17739     var handles = {}; 
17740     var autoIdSeed = 0;
17741
17742     var getId = function(el, autogen){
17743         if(typeof el == "string"){
17744             return el;
17745         }
17746         var id = el.id;
17747         if(!id && autogen !== false){
17748             id = "roodd-" + (++autoIdSeed);
17749             el.id = id;
17750         }
17751         return id;
17752     };
17753     
17754     return {
17755     /**
17756      * Register a drag drop element
17757      * @param {String|HTMLElement} element The id or DOM node to register
17758      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17759      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17760      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17761      * populated in the data object (if applicable):
17762      * <pre>
17763 Value      Description<br />
17764 ---------  ------------------------------------------<br />
17765 handles    Array of DOM nodes that trigger dragging<br />
17766            for the element being registered<br />
17767 isHandle   True if the element passed in triggers<br />
17768            dragging itself, else false
17769 </pre>
17770      */
17771         register : function(el, data){
17772             data = data || {};
17773             if(typeof el == "string"){
17774                 el = document.getElementById(el);
17775             }
17776             data.ddel = el;
17777             elements[getId(el)] = data;
17778             if(data.isHandle !== false){
17779                 handles[data.ddel.id] = data;
17780             }
17781             if(data.handles){
17782                 var hs = data.handles;
17783                 for(var i = 0, len = hs.length; i < len; i++){
17784                         handles[getId(hs[i])] = data;
17785                 }
17786             }
17787         },
17788
17789     /**
17790      * Unregister a drag drop element
17791      * @param {String|HTMLElement}  element The id or DOM node to unregister
17792      */
17793         unregister : function(el){
17794             var id = getId(el, false);
17795             var data = elements[id];
17796             if(data){
17797                 delete elements[id];
17798                 if(data.handles){
17799                     var hs = data.handles;
17800                     for(var i = 0, len = hs.length; i < len; i++){
17801                         delete handles[getId(hs[i], false)];
17802                     }
17803                 }
17804             }
17805         },
17806
17807     /**
17808      * Returns the handle registered for a DOM Node by id
17809      * @param {String|HTMLElement} id The DOM node or id to look up
17810      * @return {Object} handle The custom handle data
17811      */
17812         getHandle : function(id){
17813             if(typeof id != "string"){ // must be element?
17814                 id = id.id;
17815             }
17816             return handles[id];
17817         },
17818
17819     /**
17820      * Returns the handle that is registered for the DOM node that is the target of the event
17821      * @param {Event} e The event
17822      * @return {Object} handle The custom handle data
17823      */
17824         getHandleFromEvent : function(e){
17825             var t = Roo.lib.Event.getTarget(e);
17826             return t ? handles[t.id] : null;
17827         },
17828
17829     /**
17830      * Returns a custom data object that is registered for a DOM node by id
17831      * @param {String|HTMLElement} id The DOM node or id to look up
17832      * @return {Object} data The custom data
17833      */
17834         getTarget : function(id){
17835             if(typeof id != "string"){ // must be element?
17836                 id = id.id;
17837             }
17838             return elements[id];
17839         },
17840
17841     /**
17842      * Returns a custom data object that is registered for the DOM node that is the target of the event
17843      * @param {Event} e The event
17844      * @return {Object} data The custom data
17845      */
17846         getTargetFromEvent : function(e){
17847             var t = Roo.lib.Event.getTarget(e);
17848             return t ? elements[t.id] || handles[t.id] : null;
17849         }
17850     };
17851 }();/*
17852  * Based on:
17853  * Ext JS Library 1.1.1
17854  * Copyright(c) 2006-2007, Ext JS, LLC.
17855  *
17856  * Originally Released Under LGPL - original licence link has changed is not relivant.
17857  *
17858  * Fork - LGPL
17859  * <script type="text/javascript">
17860  */
17861  
17862
17863 /**
17864  * @class Roo.dd.StatusProxy
17865  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17866  * default drag proxy used by all Roo.dd components.
17867  * @constructor
17868  * @param {Object} config
17869  */
17870 Roo.dd.StatusProxy = function(config){
17871     Roo.apply(this, config);
17872     this.id = this.id || Roo.id();
17873     this.el = new Roo.Layer({
17874         dh: {
17875             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17876                 {tag: "div", cls: "x-dd-drop-icon"},
17877                 {tag: "div", cls: "x-dd-drag-ghost"}
17878             ]
17879         }, 
17880         shadow: !config || config.shadow !== false
17881     });
17882     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17883     this.dropStatus = this.dropNotAllowed;
17884 };
17885
17886 Roo.dd.StatusProxy.prototype = {
17887     /**
17888      * @cfg {String} dropAllowed
17889      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17890      */
17891     dropAllowed : "x-dd-drop-ok",
17892     /**
17893      * @cfg {String} dropNotAllowed
17894      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17895      */
17896     dropNotAllowed : "x-dd-drop-nodrop",
17897
17898     /**
17899      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17900      * over the current target element.
17901      * @param {String} cssClass The css class for the new drop status indicator image
17902      */
17903     setStatus : function(cssClass){
17904         cssClass = cssClass || this.dropNotAllowed;
17905         if(this.dropStatus != cssClass){
17906             this.el.replaceClass(this.dropStatus, cssClass);
17907             this.dropStatus = cssClass;
17908         }
17909     },
17910
17911     /**
17912      * Resets the status indicator to the default dropNotAllowed value
17913      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17914      */
17915     reset : function(clearGhost){
17916         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17917         this.dropStatus = this.dropNotAllowed;
17918         if(clearGhost){
17919             this.ghost.update("");
17920         }
17921     },
17922
17923     /**
17924      * Updates the contents of the ghost element
17925      * @param {String} html The html that will replace the current innerHTML of the ghost element
17926      */
17927     update : function(html){
17928         if(typeof html == "string"){
17929             this.ghost.update(html);
17930         }else{
17931             this.ghost.update("");
17932             html.style.margin = "0";
17933             this.ghost.dom.appendChild(html);
17934         }
17935         // ensure float = none set?? cant remember why though.
17936         var el = this.ghost.dom.firstChild;
17937                 if(el){
17938                         Roo.fly(el).setStyle('float', 'none');
17939                 }
17940     },
17941     
17942     /**
17943      * Returns the underlying proxy {@link Roo.Layer}
17944      * @return {Roo.Layer} el
17945     */
17946     getEl : function(){
17947         return this.el;
17948     },
17949
17950     /**
17951      * Returns the ghost element
17952      * @return {Roo.Element} el
17953      */
17954     getGhost : function(){
17955         return this.ghost;
17956     },
17957
17958     /**
17959      * Hides the proxy
17960      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17961      */
17962     hide : function(clear){
17963         this.el.hide();
17964         if(clear){
17965             this.reset(true);
17966         }
17967     },
17968
17969     /**
17970      * Stops the repair animation if it's currently running
17971      */
17972     stop : function(){
17973         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17974             this.anim.stop();
17975         }
17976     },
17977
17978     /**
17979      * Displays this proxy
17980      */
17981     show : function(){
17982         this.el.show();
17983     },
17984
17985     /**
17986      * Force the Layer to sync its shadow and shim positions to the element
17987      */
17988     sync : function(){
17989         this.el.sync();
17990     },
17991
17992     /**
17993      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17994      * invalid drop operation by the item being dragged.
17995      * @param {Array} xy The XY position of the element ([x, y])
17996      * @param {Function} callback The function to call after the repair is complete
17997      * @param {Object} scope The scope in which to execute the callback
17998      */
17999     repair : function(xy, callback, scope){
18000         this.callback = callback;
18001         this.scope = scope;
18002         if(xy && this.animRepair !== false){
18003             this.el.addClass("x-dd-drag-repair");
18004             this.el.hideUnders(true);
18005             this.anim = this.el.shift({
18006                 duration: this.repairDuration || .5,
18007                 easing: 'easeOut',
18008                 xy: xy,
18009                 stopFx: true,
18010                 callback: this.afterRepair,
18011                 scope: this
18012             });
18013         }else{
18014             this.afterRepair();
18015         }
18016     },
18017
18018     // private
18019     afterRepair : function(){
18020         this.hide(true);
18021         if(typeof this.callback == "function"){
18022             this.callback.call(this.scope || this);
18023         }
18024         this.callback = null;
18025         this.scope = null;
18026     }
18027 };/*
18028  * Based on:
18029  * Ext JS Library 1.1.1
18030  * Copyright(c) 2006-2007, Ext JS, LLC.
18031  *
18032  * Originally Released Under LGPL - original licence link has changed is not relivant.
18033  *
18034  * Fork - LGPL
18035  * <script type="text/javascript">
18036  */
18037
18038 /**
18039  * @class Roo.dd.DragSource
18040  * @extends Roo.dd.DDProxy
18041  * A simple class that provides the basic implementation needed to make any element draggable.
18042  * @constructor
18043  * @param {String/HTMLElement/Element} el The container element
18044  * @param {Object} config
18045  */
18046 Roo.dd.DragSource = function(el, config){
18047     this.el = Roo.get(el);
18048     this.dragData = {};
18049     
18050     Roo.apply(this, config);
18051     
18052     if(!this.proxy){
18053         this.proxy = new Roo.dd.StatusProxy();
18054     }
18055
18056     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18057           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18058     
18059     this.dragging = false;
18060 };
18061
18062 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18063     /**
18064      * @cfg {String} dropAllowed
18065      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18066      */
18067     dropAllowed : "x-dd-drop-ok",
18068     /**
18069      * @cfg {String} dropNotAllowed
18070      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18071      */
18072     dropNotAllowed : "x-dd-drop-nodrop",
18073
18074     /**
18075      * Returns the data object associated with this drag source
18076      * @return {Object} data An object containing arbitrary data
18077      */
18078     getDragData : function(e){
18079         return this.dragData;
18080     },
18081
18082     // private
18083     onDragEnter : function(e, id){
18084         var target = Roo.dd.DragDropMgr.getDDById(id);
18085         this.cachedTarget = target;
18086         if(this.beforeDragEnter(target, e, id) !== false){
18087             if(target.isNotifyTarget){
18088                 var status = target.notifyEnter(this, e, this.dragData);
18089                 this.proxy.setStatus(status);
18090             }else{
18091                 this.proxy.setStatus(this.dropAllowed);
18092             }
18093             
18094             if(this.afterDragEnter){
18095                 /**
18096                  * An empty function by default, but provided so that you can perform a custom action
18097                  * when the dragged item enters the drop target by providing an implementation.
18098                  * @param {Roo.dd.DragDrop} target The drop target
18099                  * @param {Event} e The event object
18100                  * @param {String} id The id of the dragged element
18101                  * @method afterDragEnter
18102                  */
18103                 this.afterDragEnter(target, e, id);
18104             }
18105         }
18106     },
18107
18108     /**
18109      * An empty function by default, but provided so that you can perform a custom action
18110      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18111      * @param {Roo.dd.DragDrop} target The drop target
18112      * @param {Event} e The event object
18113      * @param {String} id The id of the dragged element
18114      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18115      */
18116     beforeDragEnter : function(target, e, id){
18117         return true;
18118     },
18119
18120     // private
18121     alignElWithMouse: function() {
18122         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18123         this.proxy.sync();
18124     },
18125
18126     // private
18127     onDragOver : function(e, id){
18128         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18129         if(this.beforeDragOver(target, e, id) !== false){
18130             if(target.isNotifyTarget){
18131                 var status = target.notifyOver(this, e, this.dragData);
18132                 this.proxy.setStatus(status);
18133             }
18134
18135             if(this.afterDragOver){
18136                 /**
18137                  * An empty function by default, but provided so that you can perform a custom action
18138                  * while the dragged item is over the drop target by providing an implementation.
18139                  * @param {Roo.dd.DragDrop} target The drop target
18140                  * @param {Event} e The event object
18141                  * @param {String} id The id of the dragged element
18142                  * @method afterDragOver
18143                  */
18144                 this.afterDragOver(target, e, id);
18145             }
18146         }
18147     },
18148
18149     /**
18150      * An empty function by default, but provided so that you can perform a custom action
18151      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18152      * @param {Roo.dd.DragDrop} target The drop target
18153      * @param {Event} e The event object
18154      * @param {String} id The id of the dragged element
18155      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18156      */
18157     beforeDragOver : function(target, e, id){
18158         return true;
18159     },
18160
18161     // private
18162     onDragOut : function(e, id){
18163         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18164         if(this.beforeDragOut(target, e, id) !== false){
18165             if(target.isNotifyTarget){
18166                 target.notifyOut(this, e, this.dragData);
18167             }
18168             this.proxy.reset();
18169             if(this.afterDragOut){
18170                 /**
18171                  * An empty function by default, but provided so that you can perform a custom action
18172                  * after the dragged item is dragged out of the target without dropping.
18173                  * @param {Roo.dd.DragDrop} target The drop target
18174                  * @param {Event} e The event object
18175                  * @param {String} id The id of the dragged element
18176                  * @method afterDragOut
18177                  */
18178                 this.afterDragOut(target, e, id);
18179             }
18180         }
18181         this.cachedTarget = null;
18182     },
18183
18184     /**
18185      * An empty function by default, but provided so that you can perform a custom action before the dragged
18186      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18187      * @param {Roo.dd.DragDrop} target The drop target
18188      * @param {Event} e The event object
18189      * @param {String} id The id of the dragged element
18190      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18191      */
18192     beforeDragOut : function(target, e, id){
18193         return true;
18194     },
18195     
18196     // private
18197     onDragDrop : function(e, id){
18198         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18199         if(this.beforeDragDrop(target, e, id) !== false){
18200             if(target.isNotifyTarget){
18201                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18202                     this.onValidDrop(target, e, id);
18203                 }else{
18204                     this.onInvalidDrop(target, e, id);
18205                 }
18206             }else{
18207                 this.onValidDrop(target, e, id);
18208             }
18209             
18210             if(this.afterDragDrop){
18211                 /**
18212                  * An empty function by default, but provided so that you can perform a custom action
18213                  * after a valid drag drop has occurred by providing an implementation.
18214                  * @param {Roo.dd.DragDrop} target The drop target
18215                  * @param {Event} e The event object
18216                  * @param {String} id The id of the dropped element
18217                  * @method afterDragDrop
18218                  */
18219                 this.afterDragDrop(target, e, id);
18220             }
18221         }
18222         delete this.cachedTarget;
18223     },
18224
18225     /**
18226      * An empty function by default, but provided so that you can perform a custom action before the dragged
18227      * item is dropped onto the target and optionally cancel the onDragDrop.
18228      * @param {Roo.dd.DragDrop} target The drop target
18229      * @param {Event} e The event object
18230      * @param {String} id The id of the dragged element
18231      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18232      */
18233     beforeDragDrop : function(target, e, id){
18234         return true;
18235     },
18236
18237     // private
18238     onValidDrop : function(target, e, id){
18239         this.hideProxy();
18240         if(this.afterValidDrop){
18241             /**
18242              * An empty function by default, but provided so that you can perform a custom action
18243              * after a valid drop has occurred by providing an implementation.
18244              * @param {Object} target The target DD 
18245              * @param {Event} e The event object
18246              * @param {String} id The id of the dropped element
18247              * @method afterInvalidDrop
18248              */
18249             this.afterValidDrop(target, e, id);
18250         }
18251     },
18252
18253     // private
18254     getRepairXY : function(e, data){
18255         return this.el.getXY();  
18256     },
18257
18258     // private
18259     onInvalidDrop : function(target, e, id){
18260         this.beforeInvalidDrop(target, e, id);
18261         if(this.cachedTarget){
18262             if(this.cachedTarget.isNotifyTarget){
18263                 this.cachedTarget.notifyOut(this, e, this.dragData);
18264             }
18265             this.cacheTarget = null;
18266         }
18267         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18268
18269         if(this.afterInvalidDrop){
18270             /**
18271              * An empty function by default, but provided so that you can perform a custom action
18272              * after an invalid drop has occurred by providing an implementation.
18273              * @param {Event} e The event object
18274              * @param {String} id The id of the dropped element
18275              * @method afterInvalidDrop
18276              */
18277             this.afterInvalidDrop(e, id);
18278         }
18279     },
18280
18281     // private
18282     afterRepair : function(){
18283         if(Roo.enableFx){
18284             this.el.highlight(this.hlColor || "c3daf9");
18285         }
18286         this.dragging = false;
18287     },
18288
18289     /**
18290      * An empty function by default, but provided so that you can perform a custom action after an invalid
18291      * drop has occurred.
18292      * @param {Roo.dd.DragDrop} target The drop target
18293      * @param {Event} e The event object
18294      * @param {String} id The id of the dragged element
18295      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18296      */
18297     beforeInvalidDrop : function(target, e, id){
18298         return true;
18299     },
18300
18301     // private
18302     handleMouseDown : function(e){
18303         if(this.dragging) {
18304             return;
18305         }
18306         var data = this.getDragData(e);
18307         if(data && this.onBeforeDrag(data, e) !== false){
18308             this.dragData = data;
18309             this.proxy.stop();
18310             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18311         } 
18312     },
18313
18314     /**
18315      * An empty function by default, but provided so that you can perform a custom action before the initial
18316      * drag event begins and optionally cancel it.
18317      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18318      * @param {Event} e The event object
18319      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18320      */
18321     onBeforeDrag : function(data, e){
18322         return true;
18323     },
18324
18325     /**
18326      * An empty function by default, but provided so that you can perform a custom action once the initial
18327      * drag event has begun.  The drag cannot be canceled from this function.
18328      * @param {Number} x The x position of the click on the dragged object
18329      * @param {Number} y The y position of the click on the dragged object
18330      */
18331     onStartDrag : Roo.emptyFn,
18332
18333     // private - YUI override
18334     startDrag : function(x, y){
18335         this.proxy.reset();
18336         this.dragging = true;
18337         this.proxy.update("");
18338         this.onInitDrag(x, y);
18339         this.proxy.show();
18340     },
18341
18342     // private
18343     onInitDrag : function(x, y){
18344         var clone = this.el.dom.cloneNode(true);
18345         clone.id = Roo.id(); // prevent duplicate ids
18346         this.proxy.update(clone);
18347         this.onStartDrag(x, y);
18348         return true;
18349     },
18350
18351     /**
18352      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18353      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18354      */
18355     getProxy : function(){
18356         return this.proxy;  
18357     },
18358
18359     /**
18360      * Hides the drag source's {@link Roo.dd.StatusProxy}
18361      */
18362     hideProxy : function(){
18363         this.proxy.hide();  
18364         this.proxy.reset(true);
18365         this.dragging = false;
18366     },
18367
18368     // private
18369     triggerCacheRefresh : function(){
18370         Roo.dd.DDM.refreshCache(this.groups);
18371     },
18372
18373     // private - override to prevent hiding
18374     b4EndDrag: function(e) {
18375     },
18376
18377     // private - override to prevent moving
18378     endDrag : function(e){
18379         this.onEndDrag(this.dragData, e);
18380     },
18381
18382     // private
18383     onEndDrag : function(data, e){
18384     },
18385     
18386     // private - pin to cursor
18387     autoOffset : function(x, y) {
18388         this.setDelta(-12, -20);
18389     }    
18390 });/*
18391  * Based on:
18392  * Ext JS Library 1.1.1
18393  * Copyright(c) 2006-2007, Ext JS, LLC.
18394  *
18395  * Originally Released Under LGPL - original licence link has changed is not relivant.
18396  *
18397  * Fork - LGPL
18398  * <script type="text/javascript">
18399  */
18400
18401
18402 /**
18403  * @class Roo.dd.DropTarget
18404  * @extends Roo.dd.DDTarget
18405  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18406  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18407  * @constructor
18408  * @param {String/HTMLElement/Element} el The container element
18409  * @param {Object} config
18410  */
18411 Roo.dd.DropTarget = function(el, config){
18412     this.el = Roo.get(el);
18413     
18414     var listeners = false; ;
18415     if (config && config.listeners) {
18416         listeners= config.listeners;
18417         delete config.listeners;
18418     }
18419     Roo.apply(this, config);
18420     
18421     if(this.containerScroll){
18422         Roo.dd.ScrollManager.register(this.el);
18423     }
18424     this.addEvents( {
18425          /**
18426          * @scope Roo.dd.DropTarget
18427          */
18428          
18429          /**
18430          * @event enter
18431          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18432          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18433          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18434          * 
18435          * IMPORTANT : it should set this.overClass and this.dropAllowed
18436          * 
18437          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18438          * @param {Event} e The event
18439          * @param {Object} data An object containing arbitrary data supplied by the drag source
18440          */
18441         "enter" : true,
18442         
18443          /**
18444          * @event over
18445          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18446          * This method will be called on every mouse movement while the drag source is over the drop target.
18447          * This default implementation simply returns the dropAllowed config value.
18448          * 
18449          * IMPORTANT : it should set this.dropAllowed
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454          
18455          */
18456         "over" : true,
18457         /**
18458          * @event out
18459          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18460          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18461          * overClass (if any) from the drop element.
18462          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18463          * @param {Event} e The event
18464          * @param {Object} data An object containing arbitrary data supplied by the drag source
18465          */
18466          "out" : true,
18467          
18468         /**
18469          * @event drop
18470          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18471          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18472          * implementation that does something to process the drop event and returns true so that the drag source's
18473          * repair action does not run.
18474          * 
18475          * IMPORTANT : it should set this.success
18476          * 
18477          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18478          * @param {Event} e The event
18479          * @param {Object} data An object containing arbitrary data supplied by the drag source
18480         */
18481          "drop" : true
18482     });
18483             
18484      
18485     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18486         this.el.dom, 
18487         this.ddGroup || this.group,
18488         {
18489             isTarget: true,
18490             listeners : listeners || {} 
18491            
18492         
18493         }
18494     );
18495
18496 };
18497
18498 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18499     /**
18500      * @cfg {String} overClass
18501      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18502      */
18503      /**
18504      * @cfg {String} ddGroup
18505      * The drag drop group to handle drop events for
18506      */
18507      
18508     /**
18509      * @cfg {String} dropAllowed
18510      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18511      */
18512     dropAllowed : "x-dd-drop-ok",
18513     /**
18514      * @cfg {String} dropNotAllowed
18515      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18516      */
18517     dropNotAllowed : "x-dd-drop-nodrop",
18518     /**
18519      * @cfg {boolean} success
18520      * set this after drop listener.. 
18521      */
18522     success : false,
18523     /**
18524      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18525      * if the drop point is valid for over/enter..
18526      */
18527     valid : false,
18528     // private
18529     isTarget : true,
18530
18531     // private
18532     isNotifyTarget : true,
18533     
18534     /**
18535      * @hide
18536      */
18537     notifyEnter : function(dd, e, data)
18538     {
18539         this.valid = true;
18540         this.fireEvent('enter', dd, e, data);
18541         if(this.overClass){
18542             this.el.addClass(this.overClass);
18543         }
18544         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18545             this.valid ? this.dropAllowed : this.dropNotAllowed
18546         );
18547     },
18548
18549     /**
18550      * @hide
18551      */
18552     notifyOver : function(dd, e, data)
18553     {
18554         this.valid = true;
18555         this.fireEvent('over', dd, e, data);
18556         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18557             this.valid ? this.dropAllowed : this.dropNotAllowed
18558         );
18559     },
18560
18561     /**
18562      * @hide
18563      */
18564     notifyOut : function(dd, e, data)
18565     {
18566         this.fireEvent('out', dd, e, data);
18567         if(this.overClass){
18568             this.el.removeClass(this.overClass);
18569         }
18570     },
18571
18572     /**
18573      * @hide
18574      */
18575     notifyDrop : function(dd, e, data)
18576     {
18577         this.success = false;
18578         this.fireEvent('drop', dd, e, data);
18579         return this.success;
18580     }
18581 });/*
18582  * Based on:
18583  * Ext JS Library 1.1.1
18584  * Copyright(c) 2006-2007, Ext JS, LLC.
18585  *
18586  * Originally Released Under LGPL - original licence link has changed is not relivant.
18587  *
18588  * Fork - LGPL
18589  * <script type="text/javascript">
18590  */
18591
18592
18593 /**
18594  * @class Roo.dd.DragZone
18595  * @extends Roo.dd.DragSource
18596  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18597  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18598  * @constructor
18599  * @param {String/HTMLElement/Element} el The container element
18600  * @param {Object} config
18601  */
18602 Roo.dd.DragZone = function(el, config){
18603     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18604     if(this.containerScroll){
18605         Roo.dd.ScrollManager.register(this.el);
18606     }
18607 };
18608
18609 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18610     /**
18611      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18612      * for auto scrolling during drag operations.
18613      */
18614     /**
18615      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18616      * method after a failed drop (defaults to "c3daf9" - light blue)
18617      */
18618
18619     /**
18620      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18621      * for a valid target to drag based on the mouse down. Override this method
18622      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18623      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18624      * @param {EventObject} e The mouse down event
18625      * @return {Object} The dragData
18626      */
18627     getDragData : function(e){
18628         return Roo.dd.Registry.getHandleFromEvent(e);
18629     },
18630     
18631     /**
18632      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18633      * this.dragData.ddel
18634      * @param {Number} x The x position of the click on the dragged object
18635      * @param {Number} y The y position of the click on the dragged object
18636      * @return {Boolean} true to continue the drag, false to cancel
18637      */
18638     onInitDrag : function(x, y){
18639         this.proxy.update(this.dragData.ddel.cloneNode(true));
18640         this.onStartDrag(x, y);
18641         return true;
18642     },
18643     
18644     /**
18645      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18646      */
18647     afterRepair : function(){
18648         if(Roo.enableFx){
18649             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18650         }
18651         this.dragging = false;
18652     },
18653
18654     /**
18655      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18656      * the XY of this.dragData.ddel
18657      * @param {EventObject} e The mouse up event
18658      * @return {Array} The xy location (e.g. [100, 200])
18659      */
18660     getRepairXY : function(e){
18661         return Roo.Element.fly(this.dragData.ddel).getXY();  
18662     }
18663 });/*
18664  * Based on:
18665  * Ext JS Library 1.1.1
18666  * Copyright(c) 2006-2007, Ext JS, LLC.
18667  *
18668  * Originally Released Under LGPL - original licence link has changed is not relivant.
18669  *
18670  * Fork - LGPL
18671  * <script type="text/javascript">
18672  */
18673 /**
18674  * @class Roo.dd.DropZone
18675  * @extends Roo.dd.DropTarget
18676  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18677  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18678  * @constructor
18679  * @param {String/HTMLElement/Element} el The container element
18680  * @param {Object} config
18681  */
18682 Roo.dd.DropZone = function(el, config){
18683     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18684 };
18685
18686 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18687     /**
18688      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18689      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18690      * provide your own custom lookup.
18691      * @param {Event} e The event
18692      * @return {Object} data The custom data
18693      */
18694     getTargetFromEvent : function(e){
18695         return Roo.dd.Registry.getTargetFromEvent(e);
18696     },
18697
18698     /**
18699      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18700      * that it has registered.  This method has no default implementation and should be overridden to provide
18701      * node-specific processing if necessary.
18702      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18703      * {@link #getTargetFromEvent} for this node)
18704      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18705      * @param {Event} e The event
18706      * @param {Object} data An object containing arbitrary data supplied by the drag source
18707      */
18708     onNodeEnter : function(n, dd, e, data){
18709         
18710     },
18711
18712     /**
18713      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18714      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18715      * overridden to provide the proper feedback.
18716      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18717      * {@link #getTargetFromEvent} for this node)
18718      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18719      * @param {Event} e The event
18720      * @param {Object} data An object containing arbitrary data supplied by the drag source
18721      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18722      * underlying {@link Roo.dd.StatusProxy} can be updated
18723      */
18724     onNodeOver : function(n, dd, e, data){
18725         return this.dropAllowed;
18726     },
18727
18728     /**
18729      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18730      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18731      * node-specific processing if necessary.
18732      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18733      * {@link #getTargetFromEvent} for this node)
18734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18735      * @param {Event} e The event
18736      * @param {Object} data An object containing arbitrary data supplied by the drag source
18737      */
18738     onNodeOut : function(n, dd, e, data){
18739         
18740     },
18741
18742     /**
18743      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18744      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18745      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18746      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18747      * {@link #getTargetFromEvent} for this node)
18748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18749      * @param {Event} e The event
18750      * @param {Object} data An object containing arbitrary data supplied by the drag source
18751      * @return {Boolean} True if the drop was valid, else false
18752      */
18753     onNodeDrop : function(n, dd, e, data){
18754         return false;
18755     },
18756
18757     /**
18758      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18759      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18760      * it should be overridden to provide the proper feedback if necessary.
18761      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18762      * @param {Event} e The event
18763      * @param {Object} data An object containing arbitrary data supplied by the drag source
18764      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18765      * underlying {@link Roo.dd.StatusProxy} can be updated
18766      */
18767     onContainerOver : function(dd, e, data){
18768         return this.dropNotAllowed;
18769     },
18770
18771     /**
18772      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18773      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18774      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18775      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18776      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18777      * @param {Event} e The event
18778      * @param {Object} data An object containing arbitrary data supplied by the drag source
18779      * @return {Boolean} True if the drop was valid, else false
18780      */
18781     onContainerDrop : function(dd, e, data){
18782         return false;
18783     },
18784
18785     /**
18786      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18787      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18788      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18789      * you should override this method and provide a custom implementation.
18790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18791      * @param {Event} e The event
18792      * @param {Object} data An object containing arbitrary data supplied by the drag source
18793      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18794      * underlying {@link Roo.dd.StatusProxy} can be updated
18795      */
18796     notifyEnter : function(dd, e, data){
18797         return this.dropNotAllowed;
18798     },
18799
18800     /**
18801      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18802      * This method will be called on every mouse movement while the drag source is over the drop zone.
18803      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18804      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18805      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18806      * registered node, it will call {@link #onContainerOver}.
18807      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18808      * @param {Event} e The event
18809      * @param {Object} data An object containing arbitrary data supplied by the drag source
18810      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18811      * underlying {@link Roo.dd.StatusProxy} can be updated
18812      */
18813     notifyOver : function(dd, e, data){
18814         var n = this.getTargetFromEvent(e);
18815         if(!n){ // not over valid drop target
18816             if(this.lastOverNode){
18817                 this.onNodeOut(this.lastOverNode, dd, e, data);
18818                 this.lastOverNode = null;
18819             }
18820             return this.onContainerOver(dd, e, data);
18821         }
18822         if(this.lastOverNode != n){
18823             if(this.lastOverNode){
18824                 this.onNodeOut(this.lastOverNode, dd, e, data);
18825             }
18826             this.onNodeEnter(n, dd, e, data);
18827             this.lastOverNode = n;
18828         }
18829         return this.onNodeOver(n, dd, e, data);
18830     },
18831
18832     /**
18833      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18834      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18835      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18836      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18837      * @param {Event} e The event
18838      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18839      */
18840     notifyOut : function(dd, e, data){
18841         if(this.lastOverNode){
18842             this.onNodeOut(this.lastOverNode, dd, e, data);
18843             this.lastOverNode = null;
18844         }
18845     },
18846
18847     /**
18848      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18849      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18850      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18851      * otherwise it will call {@link #onContainerDrop}.
18852      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18853      * @param {Event} e The event
18854      * @param {Object} data An object containing arbitrary data supplied by the drag source
18855      * @return {Boolean} True if the drop was valid, else false
18856      */
18857     notifyDrop : function(dd, e, data){
18858         if(this.lastOverNode){
18859             this.onNodeOut(this.lastOverNode, dd, e, data);
18860             this.lastOverNode = null;
18861         }
18862         var n = this.getTargetFromEvent(e);
18863         return n ?
18864             this.onNodeDrop(n, dd, e, data) :
18865             this.onContainerDrop(dd, e, data);
18866     },
18867
18868     // private
18869     triggerCacheRefresh : function(){
18870         Roo.dd.DDM.refreshCache(this.groups);
18871     }  
18872 });/*
18873  * Based on:
18874  * Ext JS Library 1.1.1
18875  * Copyright(c) 2006-2007, Ext JS, LLC.
18876  *
18877  * Originally Released Under LGPL - original licence link has changed is not relivant.
18878  *
18879  * Fork - LGPL
18880  * <script type="text/javascript">
18881  */
18882
18883
18884 /**
18885  * @class Roo.data.SortTypes
18886  * @singleton
18887  * Defines the default sorting (casting?) comparison functions used when sorting data.
18888  */
18889 Roo.data.SortTypes = {
18890     /**
18891      * Default sort that does nothing
18892      * @param {Mixed} s The value being converted
18893      * @return {Mixed} The comparison value
18894      */
18895     none : function(s){
18896         return s;
18897     },
18898     
18899     /**
18900      * The regular expression used to strip tags
18901      * @type {RegExp}
18902      * @property
18903      */
18904     stripTagsRE : /<\/?[^>]+>/gi,
18905     
18906     /**
18907      * Strips all HTML tags to sort on text only
18908      * @param {Mixed} s The value being converted
18909      * @return {String} The comparison value
18910      */
18911     asText : function(s){
18912         return String(s).replace(this.stripTagsRE, "");
18913     },
18914     
18915     /**
18916      * Strips all HTML tags to sort on text only - Case insensitive
18917      * @param {Mixed} s The value being converted
18918      * @return {String} The comparison value
18919      */
18920     asUCText : function(s){
18921         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18922     },
18923     
18924     /**
18925      * Case insensitive string
18926      * @param {Mixed} s The value being converted
18927      * @return {String} The comparison value
18928      */
18929     asUCString : function(s) {
18930         return String(s).toUpperCase();
18931     },
18932     
18933     /**
18934      * Date sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asDate : function(s) {
18939         if(!s){
18940             return 0;
18941         }
18942         if(s instanceof Date){
18943             return s.getTime();
18944         }
18945         return Date.parse(String(s));
18946     },
18947     
18948     /**
18949      * Float sorting
18950      * @param {Mixed} s The value being converted
18951      * @return {Float} The comparison value
18952      */
18953     asFloat : function(s) {
18954         var val = parseFloat(String(s).replace(/,/g, ""));
18955         if(isNaN(val)) val = 0;
18956         return val;
18957     },
18958     
18959     /**
18960      * Integer sorting
18961      * @param {Mixed} s The value being converted
18962      * @return {Number} The comparison value
18963      */
18964     asInt : function(s) {
18965         var val = parseInt(String(s).replace(/,/g, ""));
18966         if(isNaN(val)) val = 0;
18967         return val;
18968     }
18969 };/*
18970  * Based on:
18971  * Ext JS Library 1.1.1
18972  * Copyright(c) 2006-2007, Ext JS, LLC.
18973  *
18974  * Originally Released Under LGPL - original licence link has changed is not relivant.
18975  *
18976  * Fork - LGPL
18977  * <script type="text/javascript">
18978  */
18979
18980 /**
18981 * @class Roo.data.Record
18982  * Instances of this class encapsulate both record <em>definition</em> information, and record
18983  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18984  * to access Records cached in an {@link Roo.data.Store} object.<br>
18985  * <p>
18986  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18987  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18988  * objects.<br>
18989  * <p>
18990  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18991  * @constructor
18992  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18993  * {@link #create}. The parameters are the same.
18994  * @param {Array} data An associative Array of data values keyed by the field name.
18995  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18996  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18997  * not specified an integer id is generated.
18998  */
18999 Roo.data.Record = function(data, id){
19000     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19001     this.data = data;
19002 };
19003
19004 /**
19005  * Generate a constructor for a specific record layout.
19006  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19007  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19008  * Each field definition object may contain the following properties: <ul>
19009  * <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,
19010  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19011  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19012  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19013  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19014  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19015  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19016  * this may be omitted.</p></li>
19017  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19018  * <ul><li>auto (Default, implies no conversion)</li>
19019  * <li>string</li>
19020  * <li>int</li>
19021  * <li>float</li>
19022  * <li>boolean</li>
19023  * <li>date</li></ul></p></li>
19024  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19025  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19026  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19027  * by the Reader into an object that will be stored in the Record. It is passed the
19028  * following parameters:<ul>
19029  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19030  * </ul></p></li>
19031  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19032  * </ul>
19033  * <br>usage:<br><pre><code>
19034 var TopicRecord = Roo.data.Record.create(
19035     {name: 'title', mapping: 'topic_title'},
19036     {name: 'author', mapping: 'username'},
19037     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19038     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19039     {name: 'lastPoster', mapping: 'user2'},
19040     {name: 'excerpt', mapping: 'post_text'}
19041 );
19042
19043 var myNewRecord = new TopicRecord({
19044     title: 'Do my job please',
19045     author: 'noobie',
19046     totalPosts: 1,
19047     lastPost: new Date(),
19048     lastPoster: 'Animal',
19049     excerpt: 'No way dude!'
19050 });
19051 myStore.add(myNewRecord);
19052 </code></pre>
19053  * @method create
19054  * @static
19055  */
19056 Roo.data.Record.create = function(o){
19057     var f = function(){
19058         f.superclass.constructor.apply(this, arguments);
19059     };
19060     Roo.extend(f, Roo.data.Record);
19061     var p = f.prototype;
19062     p.fields = new Roo.util.MixedCollection(false, function(field){
19063         return field.name;
19064     });
19065     for(var i = 0, len = o.length; i < len; i++){
19066         p.fields.add(new Roo.data.Field(o[i]));
19067     }
19068     f.getField = function(name){
19069         return p.fields.get(name);  
19070     };
19071     return f;
19072 };
19073
19074 Roo.data.Record.AUTO_ID = 1000;
19075 Roo.data.Record.EDIT = 'edit';
19076 Roo.data.Record.REJECT = 'reject';
19077 Roo.data.Record.COMMIT = 'commit';
19078
19079 Roo.data.Record.prototype = {
19080     /**
19081      * Readonly flag - true if this record has been modified.
19082      * @type Boolean
19083      */
19084     dirty : false,
19085     editing : false,
19086     error: null,
19087     modified: null,
19088
19089     // private
19090     join : function(store){
19091         this.store = store;
19092     },
19093
19094     /**
19095      * Set the named field to the specified value.
19096      * @param {String} name The name of the field to set.
19097      * @param {Object} value The value to set the field to.
19098      */
19099     set : function(name, value){
19100         if(this.data[name] == value){
19101             return;
19102         }
19103         this.dirty = true;
19104         if(!this.modified){
19105             this.modified = {};
19106         }
19107         if(typeof this.modified[name] == 'undefined'){
19108             this.modified[name] = this.data[name];
19109         }
19110         this.data[name] = value;
19111         if(!this.editing && this.store){
19112             this.store.afterEdit(this);
19113         }       
19114     },
19115
19116     /**
19117      * Get the value of the named field.
19118      * @param {String} name The name of the field to get the value of.
19119      * @return {Object} The value of the field.
19120      */
19121     get : function(name){
19122         return this.data[name]; 
19123     },
19124
19125     // private
19126     beginEdit : function(){
19127         this.editing = true;
19128         this.modified = {}; 
19129     },
19130
19131     // private
19132     cancelEdit : function(){
19133         this.editing = false;
19134         delete this.modified;
19135     },
19136
19137     // private
19138     endEdit : function(){
19139         this.editing = false;
19140         if(this.dirty && this.store){
19141             this.store.afterEdit(this);
19142         }
19143     },
19144
19145     /**
19146      * Usually called by the {@link Roo.data.Store} which owns the Record.
19147      * Rejects all changes made to the Record since either creation, or the last commit operation.
19148      * Modified fields are reverted to their original values.
19149      * <p>
19150      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19151      * of reject operations.
19152      */
19153     reject : function(){
19154         var m = this.modified;
19155         for(var n in m){
19156             if(typeof m[n] != "function"){
19157                 this.data[n] = m[n];
19158             }
19159         }
19160         this.dirty = false;
19161         delete this.modified;
19162         this.editing = false;
19163         if(this.store){
19164             this.store.afterReject(this);
19165         }
19166     },
19167
19168     /**
19169      * Usually called by the {@link Roo.data.Store} which owns the Record.
19170      * Commits all changes made to the Record since either creation, or the last commit operation.
19171      * <p>
19172      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19173      * of commit operations.
19174      */
19175     commit : function(){
19176         this.dirty = false;
19177         delete this.modified;
19178         this.editing = false;
19179         if(this.store){
19180             this.store.afterCommit(this);
19181         }
19182     },
19183
19184     // private
19185     hasError : function(){
19186         return this.error != null;
19187     },
19188
19189     // private
19190     clearError : function(){
19191         this.error = null;
19192     },
19193
19194     /**
19195      * Creates a copy of this record.
19196      * @param {String} id (optional) A new record id if you don't want to use this record's id
19197      * @return {Record}
19198      */
19199     copy : function(newId) {
19200         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19201     }
19202 };/*
19203  * Based on:
19204  * Ext JS Library 1.1.1
19205  * Copyright(c) 2006-2007, Ext JS, LLC.
19206  *
19207  * Originally Released Under LGPL - original licence link has changed is not relivant.
19208  *
19209  * Fork - LGPL
19210  * <script type="text/javascript">
19211  */
19212
19213
19214
19215 /**
19216  * @class Roo.data.Store
19217  * @extends Roo.util.Observable
19218  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19219  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19220  * <p>
19221  * 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
19222  * has no knowledge of the format of the data returned by the Proxy.<br>
19223  * <p>
19224  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19225  * instances from the data object. These records are cached and made available through accessor functions.
19226  * @constructor
19227  * Creates a new Store.
19228  * @param {Object} config A config object containing the objects needed for the Store to access data,
19229  * and read the data into Records.
19230  */
19231 Roo.data.Store = function(config){
19232     this.data = new Roo.util.MixedCollection(false);
19233     this.data.getKey = function(o){
19234         return o.id;
19235     };
19236     this.baseParams = {};
19237     // private
19238     this.paramNames = {
19239         "start" : "start",
19240         "limit" : "limit",
19241         "sort" : "sort",
19242         "dir" : "dir",
19243         "multisort" : "_multisort"
19244     };
19245
19246     if(config && config.data){
19247         this.inlineData = config.data;
19248         delete config.data;
19249     }
19250
19251     Roo.apply(this, config);
19252     
19253     if(this.reader){ // reader passed
19254         this.reader = Roo.factory(this.reader, Roo.data);
19255         this.reader.xmodule = this.xmodule || false;
19256         if(!this.recordType){
19257             this.recordType = this.reader.recordType;
19258         }
19259         if(this.reader.onMetaChange){
19260             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19261         }
19262     }
19263
19264     if(this.recordType){
19265         this.fields = this.recordType.prototype.fields;
19266     }
19267     this.modified = [];
19268
19269     this.addEvents({
19270         /**
19271          * @event datachanged
19272          * Fires when the data cache has changed, and a widget which is using this Store
19273          * as a Record cache should refresh its view.
19274          * @param {Store} this
19275          */
19276         datachanged : true,
19277         /**
19278          * @event metachange
19279          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19280          * @param {Store} this
19281          * @param {Object} meta The JSON metadata
19282          */
19283         metachange : true,
19284         /**
19285          * @event add
19286          * Fires when Records have been added to the Store
19287          * @param {Store} this
19288          * @param {Roo.data.Record[]} records The array of Records added
19289          * @param {Number} index The index at which the record(s) were added
19290          */
19291         add : true,
19292         /**
19293          * @event remove
19294          * Fires when a Record has been removed from the Store
19295          * @param {Store} this
19296          * @param {Roo.data.Record} record The Record that was removed
19297          * @param {Number} index The index at which the record was removed
19298          */
19299         remove : true,
19300         /**
19301          * @event update
19302          * Fires when a Record has been updated
19303          * @param {Store} this
19304          * @param {Roo.data.Record} record The Record that was updated
19305          * @param {String} operation The update operation being performed.  Value may be one of:
19306          * <pre><code>
19307  Roo.data.Record.EDIT
19308  Roo.data.Record.REJECT
19309  Roo.data.Record.COMMIT
19310          * </code></pre>
19311          */
19312         update : true,
19313         /**
19314          * @event clear
19315          * Fires when the data cache has been cleared.
19316          * @param {Store} this
19317          */
19318         clear : true,
19319         /**
19320          * @event beforeload
19321          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19322          * the load action will be canceled.
19323          * @param {Store} this
19324          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19325          */
19326         beforeload : true,
19327         /**
19328          * @event load
19329          * Fires after a new set of Records has been loaded.
19330          * @param {Store} this
19331          * @param {Roo.data.Record[]} records The Records that were loaded
19332          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19333          */
19334         load : true,
19335         /**
19336          * @event loadexception
19337          * Fires if an exception occurs in the Proxy during loading.
19338          * Called with the signature of the Proxy's "loadexception" event.
19339          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19340          * 
19341          * @param {Proxy} 
19342          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19343          * @param {Object} load options 
19344          * @param {Object} jsonData from your request (normally this contains the Exception)
19345          */
19346         loadexception : true
19347     });
19348     
19349     if(this.proxy){
19350         this.proxy = Roo.factory(this.proxy, Roo.data);
19351         this.proxy.xmodule = this.xmodule || false;
19352         this.relayEvents(this.proxy,  ["loadexception"]);
19353     }
19354     this.sortToggle = {};
19355     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19356
19357     Roo.data.Store.superclass.constructor.call(this);
19358
19359     if(this.inlineData){
19360         this.loadData(this.inlineData);
19361         delete this.inlineData;
19362     }
19363 };
19364 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19365      /**
19366     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19367     * without a remote query - used by combo/forms at present.
19368     */
19369     
19370     /**
19371     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19372     */
19373     /**
19374     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19375     */
19376     /**
19377     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19378     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19379     */
19380     /**
19381     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19382     * on any HTTP request
19383     */
19384     /**
19385     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19386     */
19387     /**
19388     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19389     */
19390     multiSort: false,
19391     /**
19392     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19393     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19394     */
19395     remoteSort : false,
19396
19397     /**
19398     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19399      * loaded or when a record is removed. (defaults to false).
19400     */
19401     pruneModifiedRecords : false,
19402
19403     // private
19404     lastOptions : null,
19405
19406     /**
19407      * Add Records to the Store and fires the add event.
19408      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19409      */
19410     add : function(records){
19411         records = [].concat(records);
19412         for(var i = 0, len = records.length; i < len; i++){
19413             records[i].join(this);
19414         }
19415         var index = this.data.length;
19416         this.data.addAll(records);
19417         this.fireEvent("add", this, records, index);
19418     },
19419
19420     /**
19421      * Remove a Record from the Store and fires the remove event.
19422      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19423      */
19424     remove : function(record){
19425         var index = this.data.indexOf(record);
19426         this.data.removeAt(index);
19427         if(this.pruneModifiedRecords){
19428             this.modified.remove(record);
19429         }
19430         this.fireEvent("remove", this, record, index);
19431     },
19432
19433     /**
19434      * Remove all Records from the Store and fires the clear event.
19435      */
19436     removeAll : function(){
19437         this.data.clear();
19438         if(this.pruneModifiedRecords){
19439             this.modified = [];
19440         }
19441         this.fireEvent("clear", this);
19442     },
19443
19444     /**
19445      * Inserts Records to the Store at the given index and fires the add event.
19446      * @param {Number} index The start index at which to insert the passed Records.
19447      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19448      */
19449     insert : function(index, records){
19450         records = [].concat(records);
19451         for(var i = 0, len = records.length; i < len; i++){
19452             this.data.insert(index, records[i]);
19453             records[i].join(this);
19454         }
19455         this.fireEvent("add", this, records, index);
19456     },
19457
19458     /**
19459      * Get the index within the cache of the passed Record.
19460      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19461      * @return {Number} The index of the passed Record. Returns -1 if not found.
19462      */
19463     indexOf : function(record){
19464         return this.data.indexOf(record);
19465     },
19466
19467     /**
19468      * Get the index within the cache of the Record with the passed id.
19469      * @param {String} id The id of the Record to find.
19470      * @return {Number} The index of the Record. Returns -1 if not found.
19471      */
19472     indexOfId : function(id){
19473         return this.data.indexOfKey(id);
19474     },
19475
19476     /**
19477      * Get the Record with the specified id.
19478      * @param {String} id The id of the Record to find.
19479      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19480      */
19481     getById : function(id){
19482         return this.data.key(id);
19483     },
19484
19485     /**
19486      * Get the Record at the specified index.
19487      * @param {Number} index The index of the Record to find.
19488      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19489      */
19490     getAt : function(index){
19491         return this.data.itemAt(index);
19492     },
19493
19494     /**
19495      * Returns a range of Records between specified indices.
19496      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19497      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19498      * @return {Roo.data.Record[]} An array of Records
19499      */
19500     getRange : function(start, end){
19501         return this.data.getRange(start, end);
19502     },
19503
19504     // private
19505     storeOptions : function(o){
19506         o = Roo.apply({}, o);
19507         delete o.callback;
19508         delete o.scope;
19509         this.lastOptions = o;
19510     },
19511
19512     /**
19513      * Loads the Record cache from the configured Proxy using the configured Reader.
19514      * <p>
19515      * If using remote paging, then the first load call must specify the <em>start</em>
19516      * and <em>limit</em> properties in the options.params property to establish the initial
19517      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19518      * <p>
19519      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19520      * and this call will return before the new data has been loaded. Perform any post-processing
19521      * in a callback function, or in a "load" event handler.</strong>
19522      * <p>
19523      * @param {Object} options An object containing properties which control loading options:<ul>
19524      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19525      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19526      * passed the following arguments:<ul>
19527      * <li>r : Roo.data.Record[]</li>
19528      * <li>options: Options object from the load call</li>
19529      * <li>success: Boolean success indicator</li></ul></li>
19530      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19531      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19532      * </ul>
19533      */
19534     load : function(options){
19535         options = options || {};
19536         if(this.fireEvent("beforeload", this, options) !== false){
19537             this.storeOptions(options);
19538             var p = Roo.apply(options.params || {}, this.baseParams);
19539             // if meta was not loaded from remote source.. try requesting it.
19540             if (!this.reader.metaFromRemote) {
19541                 p._requestMeta = 1;
19542             }
19543             if(this.sortInfo && this.remoteSort){
19544                 var pn = this.paramNames;
19545                 p[pn["sort"]] = this.sortInfo.field;
19546                 p[pn["dir"]] = this.sortInfo.direction;
19547             }
19548             if (this.multiSort) {
19549                 var pn = this.paramNames;
19550                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19551             }
19552             
19553             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19554         }
19555     },
19556
19557     /**
19558      * Reloads the Record cache from the configured Proxy using the configured Reader and
19559      * the options from the last load operation performed.
19560      * @param {Object} options (optional) An object containing properties which may override the options
19561      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19562      * the most recently used options are reused).
19563      */
19564     reload : function(options){
19565         this.load(Roo.applyIf(options||{}, this.lastOptions));
19566     },
19567
19568     // private
19569     // Called as a callback by the Reader during a load operation.
19570     loadRecords : function(o, options, success){
19571         if(!o || success === false){
19572             if(success !== false){
19573                 this.fireEvent("load", this, [], options);
19574             }
19575             if(options.callback){
19576                 options.callback.call(options.scope || this, [], options, false);
19577             }
19578             return;
19579         }
19580         // if data returned failure - throw an exception.
19581         if (o.success === false) {
19582             // show a message if no listener is registered.
19583             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19584                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19585             }
19586             // loadmask wil be hooked into this..
19587             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19588             return;
19589         }
19590         var r = o.records, t = o.totalRecords || r.length;
19591         if(!options || options.add !== true){
19592             if(this.pruneModifiedRecords){
19593                 this.modified = [];
19594             }
19595             for(var i = 0, len = r.length; i < len; i++){
19596                 r[i].join(this);
19597             }
19598             if(this.snapshot){
19599                 this.data = this.snapshot;
19600                 delete this.snapshot;
19601             }
19602             this.data.clear();
19603             this.data.addAll(r);
19604             this.totalLength = t;
19605             this.applySort();
19606             this.fireEvent("datachanged", this);
19607         }else{
19608             this.totalLength = Math.max(t, this.data.length+r.length);
19609             this.add(r);
19610         }
19611         this.fireEvent("load", this, r, options);
19612         if(options.callback){
19613             options.callback.call(options.scope || this, r, options, true);
19614         }
19615     },
19616
19617
19618     /**
19619      * Loads data from a passed data block. A Reader which understands the format of the data
19620      * must have been configured in the constructor.
19621      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19622      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19623      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19624      */
19625     loadData : function(o, append){
19626         var r = this.reader.readRecords(o);
19627         this.loadRecords(r, {add: append}, true);
19628     },
19629
19630     /**
19631      * Gets the number of cached records.
19632      * <p>
19633      * <em>If using paging, this may not be the total size of the dataset. If the data object
19634      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19635      * the data set size</em>
19636      */
19637     getCount : function(){
19638         return this.data.length || 0;
19639     },
19640
19641     /**
19642      * Gets the total number of records in the dataset as returned by the server.
19643      * <p>
19644      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19645      * the dataset size</em>
19646      */
19647     getTotalCount : function(){
19648         return this.totalLength || 0;
19649     },
19650
19651     /**
19652      * Returns the sort state of the Store as an object with two properties:
19653      * <pre><code>
19654  field {String} The name of the field by which the Records are sorted
19655  direction {String} The sort order, "ASC" or "DESC"
19656      * </code></pre>
19657      */
19658     getSortState : function(){
19659         return this.sortInfo;
19660     },
19661
19662     // private
19663     applySort : function(){
19664         if(this.sortInfo && !this.remoteSort){
19665             var s = this.sortInfo, f = s.field;
19666             var st = this.fields.get(f).sortType;
19667             var fn = function(r1, r2){
19668                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19669                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19670             };
19671             this.data.sort(s.direction, fn);
19672             if(this.snapshot && this.snapshot != this.data){
19673                 this.snapshot.sort(s.direction, fn);
19674             }
19675         }
19676     },
19677
19678     /**
19679      * Sets the default sort column and order to be used by the next load operation.
19680      * @param {String} fieldName The name of the field to sort by.
19681      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19682      */
19683     setDefaultSort : function(field, dir){
19684         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19685     },
19686
19687     /**
19688      * Sort the Records.
19689      * If remote sorting is used, the sort is performed on the server, and the cache is
19690      * reloaded. If local sorting is used, the cache is sorted internally.
19691      * @param {String} fieldName The name of the field to sort by.
19692      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19693      */
19694     sort : function(fieldName, dir){
19695         var f = this.fields.get(fieldName);
19696         if(!dir){
19697             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19698             
19699             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19700                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19701             }else{
19702                 dir = f.sortDir;
19703             }
19704         }
19705         this.sortToggle[f.name] = dir;
19706         this.sortInfo = {field: f.name, direction: dir};
19707         if(!this.remoteSort){
19708             this.applySort();
19709             this.fireEvent("datachanged", this);
19710         }else{
19711             this.load(this.lastOptions);
19712         }
19713     },
19714
19715     /**
19716      * Calls the specified function for each of the Records in the cache.
19717      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19718      * Returning <em>false</em> aborts and exits the iteration.
19719      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19720      */
19721     each : function(fn, scope){
19722         this.data.each(fn, scope);
19723     },
19724
19725     /**
19726      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19727      * (e.g., during paging).
19728      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19729      */
19730     getModifiedRecords : function(){
19731         return this.modified;
19732     },
19733
19734     // private
19735     createFilterFn : function(property, value, anyMatch){
19736         if(!value.exec){ // not a regex
19737             value = String(value);
19738             if(value.length == 0){
19739                 return false;
19740             }
19741             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19742         }
19743         return function(r){
19744             return value.test(r.data[property]);
19745         };
19746     },
19747
19748     /**
19749      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19750      * @param {String} property A field on your records
19751      * @param {Number} start The record index to start at (defaults to 0)
19752      * @param {Number} end The last record index to include (defaults to length - 1)
19753      * @return {Number} The sum
19754      */
19755     sum : function(property, start, end){
19756         var rs = this.data.items, v = 0;
19757         start = start || 0;
19758         end = (end || end === 0) ? end : rs.length-1;
19759
19760         for(var i = start; i <= end; i++){
19761             v += (rs[i].data[property] || 0);
19762         }
19763         return v;
19764     },
19765
19766     /**
19767      * Filter the records by a specified property.
19768      * @param {String} field A field on your records
19769      * @param {String/RegExp} value Either a string that the field
19770      * should start with or a RegExp to test against the field
19771      * @param {Boolean} anyMatch True to match any part not just the beginning
19772      */
19773     filter : function(property, value, anyMatch){
19774         var fn = this.createFilterFn(property, value, anyMatch);
19775         return fn ? this.filterBy(fn) : this.clearFilter();
19776     },
19777
19778     /**
19779      * Filter by a function. The specified function will be called with each
19780      * record in this data source. If the function returns true the record is included,
19781      * otherwise it is filtered.
19782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19783      * @param {Object} scope (optional) The scope of the function (defaults to this)
19784      */
19785     filterBy : function(fn, scope){
19786         this.snapshot = this.snapshot || this.data;
19787         this.data = this.queryBy(fn, scope||this);
19788         this.fireEvent("datachanged", this);
19789     },
19790
19791     /**
19792      * Query the records by a specified property.
19793      * @param {String} field A field on your records
19794      * @param {String/RegExp} value Either a string that the field
19795      * should start with or a RegExp to test against the field
19796      * @param {Boolean} anyMatch True to match any part not just the beginning
19797      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19798      */
19799     query : function(property, value, anyMatch){
19800         var fn = this.createFilterFn(property, value, anyMatch);
19801         return fn ? this.queryBy(fn) : this.data.clone();
19802     },
19803
19804     /**
19805      * Query by a function. The specified function will be called with each
19806      * record in this data source. If the function returns true the record is included
19807      * in the results.
19808      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19809      * @param {Object} scope (optional) The scope of the function (defaults to this)
19810       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19811      **/
19812     queryBy : function(fn, scope){
19813         var data = this.snapshot || this.data;
19814         return data.filterBy(fn, scope||this);
19815     },
19816
19817     /**
19818      * Collects unique values for a particular dataIndex from this store.
19819      * @param {String} dataIndex The property to collect
19820      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19821      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19822      * @return {Array} An array of the unique values
19823      **/
19824     collect : function(dataIndex, allowNull, bypassFilter){
19825         var d = (bypassFilter === true && this.snapshot) ?
19826                 this.snapshot.items : this.data.items;
19827         var v, sv, r = [], l = {};
19828         for(var i = 0, len = d.length; i < len; i++){
19829             v = d[i].data[dataIndex];
19830             sv = String(v);
19831             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19832                 l[sv] = true;
19833                 r[r.length] = v;
19834             }
19835         }
19836         return r;
19837     },
19838
19839     /**
19840      * Revert to a view of the Record cache with no filtering applied.
19841      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19842      */
19843     clearFilter : function(suppressEvent){
19844         if(this.snapshot && this.snapshot != this.data){
19845             this.data = this.snapshot;
19846             delete this.snapshot;
19847             if(suppressEvent !== true){
19848                 this.fireEvent("datachanged", this);
19849             }
19850         }
19851     },
19852
19853     // private
19854     afterEdit : function(record){
19855         if(this.modified.indexOf(record) == -1){
19856             this.modified.push(record);
19857         }
19858         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19859     },
19860     
19861     // private
19862     afterReject : function(record){
19863         this.modified.remove(record);
19864         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19865     },
19866
19867     // private
19868     afterCommit : function(record){
19869         this.modified.remove(record);
19870         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19871     },
19872
19873     /**
19874      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19875      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19876      */
19877     commitChanges : function(){
19878         var m = this.modified.slice(0);
19879         this.modified = [];
19880         for(var i = 0, len = m.length; i < len; i++){
19881             m[i].commit();
19882         }
19883     },
19884
19885     /**
19886      * Cancel outstanding changes on all changed records.
19887      */
19888     rejectChanges : function(){
19889         var m = this.modified.slice(0);
19890         this.modified = [];
19891         for(var i = 0, len = m.length; i < len; i++){
19892             m[i].reject();
19893         }
19894     },
19895
19896     onMetaChange : function(meta, rtype, o){
19897         this.recordType = rtype;
19898         this.fields = rtype.prototype.fields;
19899         delete this.snapshot;
19900         this.sortInfo = meta.sortInfo || this.sortInfo;
19901         this.modified = [];
19902         this.fireEvent('metachange', this, this.reader.meta);
19903     }
19904 });/*
19905  * Based on:
19906  * Ext JS Library 1.1.1
19907  * Copyright(c) 2006-2007, Ext JS, LLC.
19908  *
19909  * Originally Released Under LGPL - original licence link has changed is not relivant.
19910  *
19911  * Fork - LGPL
19912  * <script type="text/javascript">
19913  */
19914
19915 /**
19916  * @class Roo.data.SimpleStore
19917  * @extends Roo.data.Store
19918  * Small helper class to make creating Stores from Array data easier.
19919  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19920  * @cfg {Array} fields An array of field definition objects, or field name strings.
19921  * @cfg {Array} data The multi-dimensional array of data
19922  * @constructor
19923  * @param {Object} config
19924  */
19925 Roo.data.SimpleStore = function(config){
19926     Roo.data.SimpleStore.superclass.constructor.call(this, {
19927         isLocal : true,
19928         reader: new Roo.data.ArrayReader({
19929                 id: config.id
19930             },
19931             Roo.data.Record.create(config.fields)
19932         ),
19933         proxy : new Roo.data.MemoryProxy(config.data)
19934     });
19935     this.load();
19936 };
19937 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19938  * Based on:
19939  * Ext JS Library 1.1.1
19940  * Copyright(c) 2006-2007, Ext JS, LLC.
19941  *
19942  * Originally Released Under LGPL - original licence link has changed is not relivant.
19943  *
19944  * Fork - LGPL
19945  * <script type="text/javascript">
19946  */
19947
19948 /**
19949 /**
19950  * @extends Roo.data.Store
19951  * @class Roo.data.JsonStore
19952  * Small helper class to make creating Stores for JSON data easier. <br/>
19953 <pre><code>
19954 var store = new Roo.data.JsonStore({
19955     url: 'get-images.php',
19956     root: 'images',
19957     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19958 });
19959 </code></pre>
19960  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19961  * JsonReader and HttpProxy (unless inline data is provided).</b>
19962  * @cfg {Array} fields An array of field definition objects, or field name strings.
19963  * @constructor
19964  * @param {Object} config
19965  */
19966 Roo.data.JsonStore = function(c){
19967     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19968         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19969         reader: new Roo.data.JsonReader(c, c.fields)
19970     }));
19971 };
19972 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19973  * Based on:
19974  * Ext JS Library 1.1.1
19975  * Copyright(c) 2006-2007, Ext JS, LLC.
19976  *
19977  * Originally Released Under LGPL - original licence link has changed is not relivant.
19978  *
19979  * Fork - LGPL
19980  * <script type="text/javascript">
19981  */
19982
19983  
19984 Roo.data.Field = function(config){
19985     if(typeof config == "string"){
19986         config = {name: config};
19987     }
19988     Roo.apply(this, config);
19989     
19990     if(!this.type){
19991         this.type = "auto";
19992     }
19993     
19994     var st = Roo.data.SortTypes;
19995     // named sortTypes are supported, here we look them up
19996     if(typeof this.sortType == "string"){
19997         this.sortType = st[this.sortType];
19998     }
19999     
20000     // set default sortType for strings and dates
20001     if(!this.sortType){
20002         switch(this.type){
20003             case "string":
20004                 this.sortType = st.asUCString;
20005                 break;
20006             case "date":
20007                 this.sortType = st.asDate;
20008                 break;
20009             default:
20010                 this.sortType = st.none;
20011         }
20012     }
20013
20014     // define once
20015     var stripRe = /[\$,%]/g;
20016
20017     // prebuilt conversion function for this field, instead of
20018     // switching every time we're reading a value
20019     if(!this.convert){
20020         var cv, dateFormat = this.dateFormat;
20021         switch(this.type){
20022             case "":
20023             case "auto":
20024             case undefined:
20025                 cv = function(v){ return v; };
20026                 break;
20027             case "string":
20028                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20029                 break;
20030             case "int":
20031                 cv = function(v){
20032                     return v !== undefined && v !== null && v !== '' ?
20033                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20034                     };
20035                 break;
20036             case "float":
20037                 cv = function(v){
20038                     return v !== undefined && v !== null && v !== '' ?
20039                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20040                     };
20041                 break;
20042             case "bool":
20043             case "boolean":
20044                 cv = function(v){ return v === true || v === "true" || v == 1; };
20045                 break;
20046             case "date":
20047                 cv = function(v){
20048                     if(!v){
20049                         return '';
20050                     }
20051                     if(v instanceof Date){
20052                         return v;
20053                     }
20054                     if(dateFormat){
20055                         if(dateFormat == "timestamp"){
20056                             return new Date(v*1000);
20057                         }
20058                         return Date.parseDate(v, dateFormat);
20059                     }
20060                     var parsed = Date.parse(v);
20061                     return parsed ? new Date(parsed) : null;
20062                 };
20063              break;
20064             
20065         }
20066         this.convert = cv;
20067     }
20068 };
20069
20070 Roo.data.Field.prototype = {
20071     dateFormat: null,
20072     defaultValue: "",
20073     mapping: null,
20074     sortType : null,
20075     sortDir : "ASC"
20076 };/*
20077  * Based on:
20078  * Ext JS Library 1.1.1
20079  * Copyright(c) 2006-2007, Ext JS, LLC.
20080  *
20081  * Originally Released Under LGPL - original licence link has changed is not relivant.
20082  *
20083  * Fork - LGPL
20084  * <script type="text/javascript">
20085  */
20086  
20087 // Base class for reading structured data from a data source.  This class is intended to be
20088 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20089
20090 /**
20091  * @class Roo.data.DataReader
20092  * Base class for reading structured data from a data source.  This class is intended to be
20093  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20094  */
20095
20096 Roo.data.DataReader = function(meta, recordType){
20097     
20098     this.meta = meta;
20099     
20100     this.recordType = recordType instanceof Array ? 
20101         Roo.data.Record.create(recordType) : recordType;
20102 };
20103
20104 Roo.data.DataReader.prototype = {
20105      /**
20106      * Create an empty record
20107      * @param {Object} data (optional) - overlay some values
20108      * @return {Roo.data.Record} record created.
20109      */
20110     newRow :  function(d) {
20111         var da =  {};
20112         this.recordType.prototype.fields.each(function(c) {
20113             switch( c.type) {
20114                 case 'int' : da[c.name] = 0; break;
20115                 case 'date' : da[c.name] = new Date(); break;
20116                 case 'float' : da[c.name] = 0.0; break;
20117                 case 'boolean' : da[c.name] = false; break;
20118                 default : da[c.name] = ""; break;
20119             }
20120             
20121         });
20122         return new this.recordType(Roo.apply(da, d));
20123     }
20124     
20125 };/*
20126  * Based on:
20127  * Ext JS Library 1.1.1
20128  * Copyright(c) 2006-2007, Ext JS, LLC.
20129  *
20130  * Originally Released Under LGPL - original licence link has changed is not relivant.
20131  *
20132  * Fork - LGPL
20133  * <script type="text/javascript">
20134  */
20135
20136 /**
20137  * @class Roo.data.DataProxy
20138  * @extends Roo.data.Observable
20139  * This class is an abstract base class for implementations which provide retrieval of
20140  * unformatted data objects.<br>
20141  * <p>
20142  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20143  * (of the appropriate type which knows how to parse the data object) to provide a block of
20144  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20145  * <p>
20146  * Custom implementations must implement the load method as described in
20147  * {@link Roo.data.HttpProxy#load}.
20148  */
20149 Roo.data.DataProxy = function(){
20150     this.addEvents({
20151         /**
20152          * @event beforeload
20153          * Fires before a network request is made to retrieve a data object.
20154          * @param {Object} This DataProxy object.
20155          * @param {Object} params The params parameter to the load function.
20156          */
20157         beforeload : true,
20158         /**
20159          * @event load
20160          * Fires before the load method's callback is called.
20161          * @param {Object} This DataProxy object.
20162          * @param {Object} o The data object.
20163          * @param {Object} arg The callback argument object passed to the load function.
20164          */
20165         load : true,
20166         /**
20167          * @event loadexception
20168          * Fires if an Exception occurs during data retrieval.
20169          * @param {Object} This DataProxy object.
20170          * @param {Object} o The data object.
20171          * @param {Object} arg The callback argument object passed to the load function.
20172          * @param {Object} e The Exception.
20173          */
20174         loadexception : true
20175     });
20176     Roo.data.DataProxy.superclass.constructor.call(this);
20177 };
20178
20179 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20180
20181     /**
20182      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20183      */
20184 /*
20185  * Based on:
20186  * Ext JS Library 1.1.1
20187  * Copyright(c) 2006-2007, Ext JS, LLC.
20188  *
20189  * Originally Released Under LGPL - original licence link has changed is not relivant.
20190  *
20191  * Fork - LGPL
20192  * <script type="text/javascript">
20193  */
20194 /**
20195  * @class Roo.data.MemoryProxy
20196  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20197  * to the Reader when its load method is called.
20198  * @constructor
20199  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20200  */
20201 Roo.data.MemoryProxy = function(data){
20202     if (data.data) {
20203         data = data.data;
20204     }
20205     Roo.data.MemoryProxy.superclass.constructor.call(this);
20206     this.data = data;
20207 };
20208
20209 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20210     /**
20211      * Load data from the requested source (in this case an in-memory
20212      * data object passed to the constructor), read the data object into
20213      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20214      * process that block using the passed callback.
20215      * @param {Object} params This parameter is not used by the MemoryProxy class.
20216      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20217      * object into a block of Roo.data.Records.
20218      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20219      * The function must be passed <ul>
20220      * <li>The Record block object</li>
20221      * <li>The "arg" argument from the load function</li>
20222      * <li>A boolean success indicator</li>
20223      * </ul>
20224      * @param {Object} scope The scope in which to call the callback
20225      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20226      */
20227     load : function(params, reader, callback, scope, arg){
20228         params = params || {};
20229         var result;
20230         try {
20231             result = reader.readRecords(this.data);
20232         }catch(e){
20233             this.fireEvent("loadexception", this, arg, null, e);
20234             callback.call(scope, null, arg, false);
20235             return;
20236         }
20237         callback.call(scope, result, arg, true);
20238     },
20239     
20240     // private
20241     update : function(params, records){
20242         
20243     }
20244 });/*
20245  * Based on:
20246  * Ext JS Library 1.1.1
20247  * Copyright(c) 2006-2007, Ext JS, LLC.
20248  *
20249  * Originally Released Under LGPL - original licence link has changed is not relivant.
20250  *
20251  * Fork - LGPL
20252  * <script type="text/javascript">
20253  */
20254 /**
20255  * @class Roo.data.HttpProxy
20256  * @extends Roo.data.DataProxy
20257  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20258  * configured to reference a certain URL.<br><br>
20259  * <p>
20260  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20261  * from which the running page was served.<br><br>
20262  * <p>
20263  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20264  * <p>
20265  * Be aware that to enable the browser to parse an XML document, the server must set
20266  * the Content-Type header in the HTTP response to "text/xml".
20267  * @constructor
20268  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20269  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20270  * will be used to make the request.
20271  */
20272 Roo.data.HttpProxy = function(conn){
20273     Roo.data.HttpProxy.superclass.constructor.call(this);
20274     // is conn a conn config or a real conn?
20275     this.conn = conn;
20276     this.useAjax = !conn || !conn.events;
20277   
20278 };
20279
20280 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20281     // thse are take from connection...
20282     
20283     /**
20284      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20285      */
20286     /**
20287      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20288      * extra parameters to each request made by this object. (defaults to undefined)
20289      */
20290     /**
20291      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20292      *  to each request made by this object. (defaults to undefined)
20293      */
20294     /**
20295      * @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)
20296      */
20297     /**
20298      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20299      */
20300      /**
20301      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20302      * @type Boolean
20303      */
20304   
20305
20306     /**
20307      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20308      * @type Boolean
20309      */
20310     /**
20311      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20312      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20313      * a finer-grained basis than the DataProxy events.
20314      */
20315     getConnection : function(){
20316         return this.useAjax ? Roo.Ajax : this.conn;
20317     },
20318
20319     /**
20320      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20321      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20322      * process that block using the passed callback.
20323      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20324      * for the request to the remote server.
20325      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20326      * object into a block of Roo.data.Records.
20327      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20328      * The function must be passed <ul>
20329      * <li>The Record block object</li>
20330      * <li>The "arg" argument from the load function</li>
20331      * <li>A boolean success indicator</li>
20332      * </ul>
20333      * @param {Object} scope The scope in which to call the callback
20334      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20335      */
20336     load : function(params, reader, callback, scope, arg){
20337         if(this.fireEvent("beforeload", this, params) !== false){
20338             var  o = {
20339                 params : params || {},
20340                 request: {
20341                     callback : callback,
20342                     scope : scope,
20343                     arg : arg
20344                 },
20345                 reader: reader,
20346                 callback : this.loadResponse,
20347                 scope: this
20348             };
20349             if(this.useAjax){
20350                 Roo.applyIf(o, this.conn);
20351                 if(this.activeRequest){
20352                     Roo.Ajax.abort(this.activeRequest);
20353                 }
20354                 this.activeRequest = Roo.Ajax.request(o);
20355             }else{
20356                 this.conn.request(o);
20357             }
20358         }else{
20359             callback.call(scope||this, null, arg, false);
20360         }
20361     },
20362
20363     // private
20364     loadResponse : function(o, success, response){
20365         delete this.activeRequest;
20366         if(!success){
20367             this.fireEvent("loadexception", this, o, response);
20368             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20369             return;
20370         }
20371         var result;
20372         try {
20373             result = o.reader.read(response);
20374         }catch(e){
20375             this.fireEvent("loadexception", this, o, response, e);
20376             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20377             return;
20378         }
20379         
20380         this.fireEvent("load", this, o, o.request.arg);
20381         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20382     },
20383
20384     // private
20385     update : function(dataSet){
20386
20387     },
20388
20389     // private
20390     updateResponse : function(dataSet){
20391
20392     }
20393 });/*
20394  * Based on:
20395  * Ext JS Library 1.1.1
20396  * Copyright(c) 2006-2007, Ext JS, LLC.
20397  *
20398  * Originally Released Under LGPL - original licence link has changed is not relivant.
20399  *
20400  * Fork - LGPL
20401  * <script type="text/javascript">
20402  */
20403
20404 /**
20405  * @class Roo.data.ScriptTagProxy
20406  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20407  * other than the originating domain of the running page.<br><br>
20408  * <p>
20409  * <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
20410  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20411  * <p>
20412  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20413  * source code that is used as the source inside a &lt;script> tag.<br><br>
20414  * <p>
20415  * In order for the browser to process the returned data, the server must wrap the data object
20416  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20417  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20418  * depending on whether the callback name was passed:
20419  * <p>
20420  * <pre><code>
20421 boolean scriptTag = false;
20422 String cb = request.getParameter("callback");
20423 if (cb != null) {
20424     scriptTag = true;
20425     response.setContentType("text/javascript");
20426 } else {
20427     response.setContentType("application/x-json");
20428 }
20429 Writer out = response.getWriter();
20430 if (scriptTag) {
20431     out.write(cb + "(");
20432 }
20433 out.print(dataBlock.toJsonString());
20434 if (scriptTag) {
20435     out.write(");");
20436 }
20437 </pre></code>
20438  *
20439  * @constructor
20440  * @param {Object} config A configuration object.
20441  */
20442 Roo.data.ScriptTagProxy = function(config){
20443     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20444     Roo.apply(this, config);
20445     this.head = document.getElementsByTagName("head")[0];
20446 };
20447
20448 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20449
20450 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20451     /**
20452      * @cfg {String} url The URL from which to request the data object.
20453      */
20454     /**
20455      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20456      */
20457     timeout : 30000,
20458     /**
20459      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20460      * the server the name of the callback function set up by the load call to process the returned data object.
20461      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20462      * javascript output which calls this named function passing the data object as its only parameter.
20463      */
20464     callbackParam : "callback",
20465     /**
20466      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20467      * name to the request.
20468      */
20469     nocache : true,
20470
20471     /**
20472      * Load data from the configured URL, read the data object into
20473      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20474      * process that block using the passed callback.
20475      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20476      * for the request to the remote server.
20477      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20478      * object into a block of Roo.data.Records.
20479      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20480      * The function must be passed <ul>
20481      * <li>The Record block object</li>
20482      * <li>The "arg" argument from the load function</li>
20483      * <li>A boolean success indicator</li>
20484      * </ul>
20485      * @param {Object} scope The scope in which to call the callback
20486      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20487      */
20488     load : function(params, reader, callback, scope, arg){
20489         if(this.fireEvent("beforeload", this, params) !== false){
20490
20491             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20492
20493             var url = this.url;
20494             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20495             if(this.nocache){
20496                 url += "&_dc=" + (new Date().getTime());
20497             }
20498             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20499             var trans = {
20500                 id : transId,
20501                 cb : "stcCallback"+transId,
20502                 scriptId : "stcScript"+transId,
20503                 params : params,
20504                 arg : arg,
20505                 url : url,
20506                 callback : callback,
20507                 scope : scope,
20508                 reader : reader
20509             };
20510             var conn = this;
20511
20512             window[trans.cb] = function(o){
20513                 conn.handleResponse(o, trans);
20514             };
20515
20516             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20517
20518             if(this.autoAbort !== false){
20519                 this.abort();
20520             }
20521
20522             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20523
20524             var script = document.createElement("script");
20525             script.setAttribute("src", url);
20526             script.setAttribute("type", "text/javascript");
20527             script.setAttribute("id", trans.scriptId);
20528             this.head.appendChild(script);
20529
20530             this.trans = trans;
20531         }else{
20532             callback.call(scope||this, null, arg, false);
20533         }
20534     },
20535
20536     // private
20537     isLoading : function(){
20538         return this.trans ? true : false;
20539     },
20540
20541     /**
20542      * Abort the current server request.
20543      */
20544     abort : function(){
20545         if(this.isLoading()){
20546             this.destroyTrans(this.trans);
20547         }
20548     },
20549
20550     // private
20551     destroyTrans : function(trans, isLoaded){
20552         this.head.removeChild(document.getElementById(trans.scriptId));
20553         clearTimeout(trans.timeoutId);
20554         if(isLoaded){
20555             window[trans.cb] = undefined;
20556             try{
20557                 delete window[trans.cb];
20558             }catch(e){}
20559         }else{
20560             // if hasn't been loaded, wait for load to remove it to prevent script error
20561             window[trans.cb] = function(){
20562                 window[trans.cb] = undefined;
20563                 try{
20564                     delete window[trans.cb];
20565                 }catch(e){}
20566             };
20567         }
20568     },
20569
20570     // private
20571     handleResponse : function(o, trans){
20572         this.trans = false;
20573         this.destroyTrans(trans, true);
20574         var result;
20575         try {
20576             result = trans.reader.readRecords(o);
20577         }catch(e){
20578             this.fireEvent("loadexception", this, o, trans.arg, e);
20579             trans.callback.call(trans.scope||window, null, trans.arg, false);
20580             return;
20581         }
20582         this.fireEvent("load", this, o, trans.arg);
20583         trans.callback.call(trans.scope||window, result, trans.arg, true);
20584     },
20585
20586     // private
20587     handleFailure : function(trans){
20588         this.trans = false;
20589         this.destroyTrans(trans, false);
20590         this.fireEvent("loadexception", this, null, trans.arg);
20591         trans.callback.call(trans.scope||window, null, trans.arg, false);
20592     }
20593 });/*
20594  * Based on:
20595  * Ext JS Library 1.1.1
20596  * Copyright(c) 2006-2007, Ext JS, LLC.
20597  *
20598  * Originally Released Under LGPL - original licence link has changed is not relivant.
20599  *
20600  * Fork - LGPL
20601  * <script type="text/javascript">
20602  */
20603
20604 /**
20605  * @class Roo.data.JsonReader
20606  * @extends Roo.data.DataReader
20607  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20608  * based on mappings in a provided Roo.data.Record constructor.
20609  * 
20610  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20611  * in the reply previously. 
20612  * 
20613  * <p>
20614  * Example code:
20615  * <pre><code>
20616 var RecordDef = Roo.data.Record.create([
20617     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20618     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20619 ]);
20620 var myReader = new Roo.data.JsonReader({
20621     totalProperty: "results",    // The property which contains the total dataset size (optional)
20622     root: "rows",                // The property which contains an Array of row objects
20623     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20624 }, RecordDef);
20625 </code></pre>
20626  * <p>
20627  * This would consume a JSON file like this:
20628  * <pre><code>
20629 { 'results': 2, 'rows': [
20630     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20631     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20632 }
20633 </code></pre>
20634  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20635  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20636  * paged from the remote server.
20637  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20638  * @cfg {String} root name of the property which contains the Array of row objects.
20639  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20640  * @constructor
20641  * Create a new JsonReader
20642  * @param {Object} meta Metadata configuration options
20643  * @param {Object} recordType Either an Array of field definition objects,
20644  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20645  */
20646 Roo.data.JsonReader = function(meta, recordType){
20647     
20648     meta = meta || {};
20649     // set some defaults:
20650     Roo.applyIf(meta, {
20651         totalProperty: 'total',
20652         successProperty : 'success',
20653         root : 'data',
20654         id : 'id'
20655     });
20656     
20657     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20658 };
20659 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20660     
20661     /**
20662      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20663      * Used by Store query builder to append _requestMeta to params.
20664      * 
20665      */
20666     metaFromRemote : false,
20667     /**
20668      * This method is only used by a DataProxy which has retrieved data from a remote server.
20669      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20670      * @return {Object} data A data block which is used by an Roo.data.Store object as
20671      * a cache of Roo.data.Records.
20672      */
20673     read : function(response){
20674         var json = response.responseText;
20675        
20676         var o = /* eval:var:o */ eval("("+json+")");
20677         if(!o) {
20678             throw {message: "JsonReader.read: Json object not found"};
20679         }
20680         
20681         if(o.metaData){
20682             
20683             delete this.ef;
20684             this.metaFromRemote = true;
20685             this.meta = o.metaData;
20686             this.recordType = Roo.data.Record.create(o.metaData.fields);
20687             this.onMetaChange(this.meta, this.recordType, o);
20688         }
20689         return this.readRecords(o);
20690     },
20691
20692     // private function a store will implement
20693     onMetaChange : function(meta, recordType, o){
20694
20695     },
20696
20697     /**
20698          * @ignore
20699          */
20700     simpleAccess: function(obj, subsc) {
20701         return obj[subsc];
20702     },
20703
20704         /**
20705          * @ignore
20706          */
20707     getJsonAccessor: function(){
20708         var re = /[\[\.]/;
20709         return function(expr) {
20710             try {
20711                 return(re.test(expr))
20712                     ? new Function("obj", "return obj." + expr)
20713                     : function(obj){
20714                         return obj[expr];
20715                     };
20716             } catch(e){}
20717             return Roo.emptyFn;
20718         };
20719     }(),
20720
20721     /**
20722      * Create a data block containing Roo.data.Records from an XML document.
20723      * @param {Object} o An object which contains an Array of row objects in the property specified
20724      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20725      * which contains the total size of the dataset.
20726      * @return {Object} data A data block which is used by an Roo.data.Store object as
20727      * a cache of Roo.data.Records.
20728      */
20729     readRecords : function(o){
20730         /**
20731          * After any data loads, the raw JSON data is available for further custom processing.
20732          * @type Object
20733          */
20734         this.jsonData = o;
20735         var s = this.meta, Record = this.recordType,
20736             f = Record.prototype.fields, fi = f.items, fl = f.length;
20737
20738 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20739         if (!this.ef) {
20740             if(s.totalProperty) {
20741                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20742                 }
20743                 if(s.successProperty) {
20744                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20745                 }
20746                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20747                 if (s.id) {
20748                         var g = this.getJsonAccessor(s.id);
20749                         this.getId = function(rec) {
20750                                 var r = g(rec);
20751                                 return (r === undefined || r === "") ? null : r;
20752                         };
20753                 } else {
20754                         this.getId = function(){return null;};
20755                 }
20756             this.ef = [];
20757             for(var jj = 0; jj < fl; jj++){
20758                 f = fi[jj];
20759                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20760                 this.ef[jj] = this.getJsonAccessor(map);
20761             }
20762         }
20763
20764         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20765         if(s.totalProperty){
20766             var vt = parseInt(this.getTotal(o), 10);
20767             if(!isNaN(vt)){
20768                 totalRecords = vt;
20769             }
20770         }
20771         if(s.successProperty){
20772             var vs = this.getSuccess(o);
20773             if(vs === false || vs === 'false'){
20774                 success = false;
20775             }
20776         }
20777         var records = [];
20778             for(var i = 0; i < c; i++){
20779                     var n = root[i];
20780                 var values = {};
20781                 var id = this.getId(n);
20782                 for(var j = 0; j < fl; j++){
20783                     f = fi[j];
20784                 var v = this.ef[j](n);
20785                 if (!f.convert) {
20786                     Roo.log('missing convert for ' + f.name);
20787                     Roo.log(f);
20788                     continue;
20789                 }
20790                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20791                 }
20792                 var record = new Record(values, id);
20793                 record.json = n;
20794                 records[i] = record;
20795             }
20796             return {
20797                 success : success,
20798                 records : records,
20799                 totalRecords : totalRecords
20800             };
20801     }
20802 });/*
20803  * Based on:
20804  * Ext JS Library 1.1.1
20805  * Copyright(c) 2006-2007, Ext JS, LLC.
20806  *
20807  * Originally Released Under LGPL - original licence link has changed is not relivant.
20808  *
20809  * Fork - LGPL
20810  * <script type="text/javascript">
20811  */
20812
20813 /**
20814  * @class Roo.data.XmlReader
20815  * @extends Roo.data.DataReader
20816  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20817  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20818  * <p>
20819  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20820  * header in the HTTP response must be set to "text/xml".</em>
20821  * <p>
20822  * Example code:
20823  * <pre><code>
20824 var RecordDef = Roo.data.Record.create([
20825    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20826    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20827 ]);
20828 var myReader = new Roo.data.XmlReader({
20829    totalRecords: "results", // The element which contains the total dataset size (optional)
20830    record: "row",           // The repeated element which contains row information
20831    id: "id"                 // The element within the row that provides an ID for the record (optional)
20832 }, RecordDef);
20833 </code></pre>
20834  * <p>
20835  * This would consume an XML file like this:
20836  * <pre><code>
20837 &lt;?xml?>
20838 &lt;dataset>
20839  &lt;results>2&lt;/results>
20840  &lt;row>
20841    &lt;id>1&lt;/id>
20842    &lt;name>Bill&lt;/name>
20843    &lt;occupation>Gardener&lt;/occupation>
20844  &lt;/row>
20845  &lt;row>
20846    &lt;id>2&lt;/id>
20847    &lt;name>Ben&lt;/name>
20848    &lt;occupation>Horticulturalist&lt;/occupation>
20849  &lt;/row>
20850 &lt;/dataset>
20851 </code></pre>
20852  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20853  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20854  * paged from the remote server.
20855  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20856  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20857  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20858  * a record identifier value.
20859  * @constructor
20860  * Create a new XmlReader
20861  * @param {Object} meta Metadata configuration options
20862  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20863  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20864  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20865  */
20866 Roo.data.XmlReader = function(meta, recordType){
20867     meta = meta || {};
20868     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20869 };
20870 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20871     /**
20872      * This method is only used by a DataProxy which has retrieved data from a remote server.
20873          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20874          * to contain a method called 'responseXML' that returns an XML document object.
20875      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20876      * a cache of Roo.data.Records.
20877      */
20878     read : function(response){
20879         var doc = response.responseXML;
20880         if(!doc) {
20881             throw {message: "XmlReader.read: XML Document not available"};
20882         }
20883         return this.readRecords(doc);
20884     },
20885
20886     /**
20887      * Create a data block containing Roo.data.Records from an XML document.
20888          * @param {Object} doc A parsed XML document.
20889      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20890      * a cache of Roo.data.Records.
20891      */
20892     readRecords : function(doc){
20893         /**
20894          * After any data loads/reads, the raw XML Document is available for further custom processing.
20895          * @type XMLDocument
20896          */
20897         this.xmlData = doc;
20898         var root = doc.documentElement || doc;
20899         var q = Roo.DomQuery;
20900         var recordType = this.recordType, fields = recordType.prototype.fields;
20901         var sid = this.meta.id;
20902         var totalRecords = 0, success = true;
20903         if(this.meta.totalRecords){
20904             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20905         }
20906         
20907         if(this.meta.success){
20908             var sv = q.selectValue(this.meta.success, root, true);
20909             success = sv !== false && sv !== 'false';
20910         }
20911         var records = [];
20912         var ns = q.select(this.meta.record, root);
20913         for(var i = 0, len = ns.length; i < len; i++) {
20914                 var n = ns[i];
20915                 var values = {};
20916                 var id = sid ? q.selectValue(sid, n) : undefined;
20917                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20918                     var f = fields.items[j];
20919                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20920                     v = f.convert(v);
20921                     values[f.name] = v;
20922                 }
20923                 var record = new recordType(values, id);
20924                 record.node = n;
20925                 records[records.length] = record;
20926             }
20927
20928             return {
20929                 success : success,
20930                 records : records,
20931                 totalRecords : totalRecords || records.length
20932             };
20933     }
20934 });/*
20935  * Based on:
20936  * Ext JS Library 1.1.1
20937  * Copyright(c) 2006-2007, Ext JS, LLC.
20938  *
20939  * Originally Released Under LGPL - original licence link has changed is not relivant.
20940  *
20941  * Fork - LGPL
20942  * <script type="text/javascript">
20943  */
20944
20945 /**
20946  * @class Roo.data.ArrayReader
20947  * @extends Roo.data.DataReader
20948  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20949  * Each element of that Array represents a row of data fields. The
20950  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20951  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20952  * <p>
20953  * Example code:.
20954  * <pre><code>
20955 var RecordDef = Roo.data.Record.create([
20956     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20957     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20958 ]);
20959 var myReader = new Roo.data.ArrayReader({
20960     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20961 }, RecordDef);
20962 </code></pre>
20963  * <p>
20964  * This would consume an Array like this:
20965  * <pre><code>
20966 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20967   </code></pre>
20968  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20969  * @constructor
20970  * Create a new JsonReader
20971  * @param {Object} meta Metadata configuration options.
20972  * @param {Object} recordType Either an Array of field definition objects
20973  * as specified to {@link Roo.data.Record#create},
20974  * or an {@link Roo.data.Record} object
20975  * created using {@link Roo.data.Record#create}.
20976  */
20977 Roo.data.ArrayReader = function(meta, recordType){
20978     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20979 };
20980
20981 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20982     /**
20983      * Create a data block containing Roo.data.Records from an XML document.
20984      * @param {Object} o An Array of row objects which represents the dataset.
20985      * @return {Object} data A data block which is used by an Roo.data.Store object as
20986      * a cache of Roo.data.Records.
20987      */
20988     readRecords : function(o){
20989         var sid = this.meta ? this.meta.id : null;
20990         var recordType = this.recordType, fields = recordType.prototype.fields;
20991         var records = [];
20992         var root = o;
20993             for(var i = 0; i < root.length; i++){
20994                     var n = root[i];
20995                 var values = {};
20996                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20997                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20998                 var f = fields.items[j];
20999                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21000                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21001                 v = f.convert(v);
21002                 values[f.name] = v;
21003             }
21004                 var record = new recordType(values, id);
21005                 record.json = n;
21006                 records[records.length] = record;
21007             }
21008             return {
21009                 records : records,
21010                 totalRecords : records.length
21011             };
21012     }
21013 });/*
21014  * Based on:
21015  * Ext JS Library 1.1.1
21016  * Copyright(c) 2006-2007, Ext JS, LLC.
21017  *
21018  * Originally Released Under LGPL - original licence link has changed is not relivant.
21019  *
21020  * Fork - LGPL
21021  * <script type="text/javascript">
21022  */
21023
21024
21025 /**
21026  * @class Roo.data.Tree
21027  * @extends Roo.util.Observable
21028  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21029  * in the tree have most standard DOM functionality.
21030  * @constructor
21031  * @param {Node} root (optional) The root node
21032  */
21033 Roo.data.Tree = function(root){
21034    this.nodeHash = {};
21035    /**
21036     * The root node for this tree
21037     * @type Node
21038     */
21039    this.root = null;
21040    if(root){
21041        this.setRootNode(root);
21042    }
21043    this.addEvents({
21044        /**
21045         * @event append
21046         * Fires when a new child node is appended to a node in this tree.
21047         * @param {Tree} tree The owner tree
21048         * @param {Node} parent The parent node
21049         * @param {Node} node The newly appended node
21050         * @param {Number} index The index of the newly appended node
21051         */
21052        "append" : true,
21053        /**
21054         * @event remove
21055         * Fires when a child node is removed from a node in this tree.
21056         * @param {Tree} tree The owner tree
21057         * @param {Node} parent The parent node
21058         * @param {Node} node The child node removed
21059         */
21060        "remove" : true,
21061        /**
21062         * @event move
21063         * Fires when a node is moved to a new location in the tree
21064         * @param {Tree} tree The owner tree
21065         * @param {Node} node The node moved
21066         * @param {Node} oldParent The old parent of this node
21067         * @param {Node} newParent The new parent of this node
21068         * @param {Number} index The index it was moved to
21069         */
21070        "move" : true,
21071        /**
21072         * @event insert
21073         * Fires when a new child node is inserted in a node in this tree.
21074         * @param {Tree} tree The owner tree
21075         * @param {Node} parent The parent node
21076         * @param {Node} node The child node inserted
21077         * @param {Node} refNode The child node the node was inserted before
21078         */
21079        "insert" : true,
21080        /**
21081         * @event beforeappend
21082         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21083         * @param {Tree} tree The owner tree
21084         * @param {Node} parent The parent node
21085         * @param {Node} node The child node to be appended
21086         */
21087        "beforeappend" : true,
21088        /**
21089         * @event beforeremove
21090         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21091         * @param {Tree} tree The owner tree
21092         * @param {Node} parent The parent node
21093         * @param {Node} node The child node to be removed
21094         */
21095        "beforeremove" : true,
21096        /**
21097         * @event beforemove
21098         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21099         * @param {Tree} tree The owner tree
21100         * @param {Node} node The node being moved
21101         * @param {Node} oldParent The parent of the node
21102         * @param {Node} newParent The new parent the node is moving to
21103         * @param {Number} index The index it is being moved to
21104         */
21105        "beforemove" : true,
21106        /**
21107         * @event beforeinsert
21108         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21109         * @param {Tree} tree The owner tree
21110         * @param {Node} parent The parent node
21111         * @param {Node} node The child node to be inserted
21112         * @param {Node} refNode The child node the node is being inserted before
21113         */
21114        "beforeinsert" : true
21115    });
21116
21117     Roo.data.Tree.superclass.constructor.call(this);
21118 };
21119
21120 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21121     pathSeparator: "/",
21122
21123     proxyNodeEvent : function(){
21124         return this.fireEvent.apply(this, arguments);
21125     },
21126
21127     /**
21128      * Returns the root node for this tree.
21129      * @return {Node}
21130      */
21131     getRootNode : function(){
21132         return this.root;
21133     },
21134
21135     /**
21136      * Sets the root node for this tree.
21137      * @param {Node} node
21138      * @return {Node}
21139      */
21140     setRootNode : function(node){
21141         this.root = node;
21142         node.ownerTree = this;
21143         node.isRoot = true;
21144         this.registerNode(node);
21145         return node;
21146     },
21147
21148     /**
21149      * Gets a node in this tree by its id.
21150      * @param {String} id
21151      * @return {Node}
21152      */
21153     getNodeById : function(id){
21154         return this.nodeHash[id];
21155     },
21156
21157     registerNode : function(node){
21158         this.nodeHash[node.id] = node;
21159     },
21160
21161     unregisterNode : function(node){
21162         delete this.nodeHash[node.id];
21163     },
21164
21165     toString : function(){
21166         return "[Tree"+(this.id?" "+this.id:"")+"]";
21167     }
21168 });
21169
21170 /**
21171  * @class Roo.data.Node
21172  * @extends Roo.util.Observable
21173  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21174  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21175  * @constructor
21176  * @param {Object} attributes The attributes/config for the node
21177  */
21178 Roo.data.Node = function(attributes){
21179     /**
21180      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21181      * @type {Object}
21182      */
21183     this.attributes = attributes || {};
21184     this.leaf = this.attributes.leaf;
21185     /**
21186      * The node id. @type String
21187      */
21188     this.id = this.attributes.id;
21189     if(!this.id){
21190         this.id = Roo.id(null, "ynode-");
21191         this.attributes.id = this.id;
21192     }
21193     /**
21194      * All child nodes of this node. @type Array
21195      */
21196     this.childNodes = [];
21197     if(!this.childNodes.indexOf){ // indexOf is a must
21198         this.childNodes.indexOf = function(o){
21199             for(var i = 0, len = this.length; i < len; i++){
21200                 if(this[i] == o) {
21201                     return i;
21202                 }
21203             }
21204             return -1;
21205         };
21206     }
21207     /**
21208      * The parent node for this node. @type Node
21209      */
21210     this.parentNode = null;
21211     /**
21212      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21213      */
21214     this.firstChild = null;
21215     /**
21216      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21217      */
21218     this.lastChild = null;
21219     /**
21220      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21221      */
21222     this.previousSibling = null;
21223     /**
21224      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21225      */
21226     this.nextSibling = null;
21227
21228     this.addEvents({
21229        /**
21230         * @event append
21231         * Fires when a new child node is appended
21232         * @param {Tree} tree The owner tree
21233         * @param {Node} this This node
21234         * @param {Node} node The newly appended node
21235         * @param {Number} index The index of the newly appended node
21236         */
21237        "append" : true,
21238        /**
21239         * @event remove
21240         * Fires when a child node is removed
21241         * @param {Tree} tree The owner tree
21242         * @param {Node} this This node
21243         * @param {Node} node The removed node
21244         */
21245        "remove" : true,
21246        /**
21247         * @event move
21248         * Fires when this node is moved to a new location in the tree
21249         * @param {Tree} tree The owner tree
21250         * @param {Node} this This node
21251         * @param {Node} oldParent The old parent of this node
21252         * @param {Node} newParent The new parent of this node
21253         * @param {Number} index The index it was moved to
21254         */
21255        "move" : true,
21256        /**
21257         * @event insert
21258         * Fires when a new child node is inserted.
21259         * @param {Tree} tree The owner tree
21260         * @param {Node} this This node
21261         * @param {Node} node The child node inserted
21262         * @param {Node} refNode The child node the node was inserted before
21263         */
21264        "insert" : true,
21265        /**
21266         * @event beforeappend
21267         * Fires before a new child is appended, return false to cancel the append.
21268         * @param {Tree} tree The owner tree
21269         * @param {Node} this This node
21270         * @param {Node} node The child node to be appended
21271         */
21272        "beforeappend" : true,
21273        /**
21274         * @event beforeremove
21275         * Fires before a child is removed, return false to cancel the remove.
21276         * @param {Tree} tree The owner tree
21277         * @param {Node} this This node
21278         * @param {Node} node The child node to be removed
21279         */
21280        "beforeremove" : true,
21281        /**
21282         * @event beforemove
21283         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21284         * @param {Tree} tree The owner tree
21285         * @param {Node} this This node
21286         * @param {Node} oldParent The parent of this node
21287         * @param {Node} newParent The new parent this node is moving to
21288         * @param {Number} index The index it is being moved to
21289         */
21290        "beforemove" : true,
21291        /**
21292         * @event beforeinsert
21293         * Fires before a new child is inserted, return false to cancel the insert.
21294         * @param {Tree} tree The owner tree
21295         * @param {Node} this This node
21296         * @param {Node} node The child node to be inserted
21297         * @param {Node} refNode The child node the node is being inserted before
21298         */
21299        "beforeinsert" : true
21300    });
21301     this.listeners = this.attributes.listeners;
21302     Roo.data.Node.superclass.constructor.call(this);
21303 };
21304
21305 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21306     fireEvent : function(evtName){
21307         // first do standard event for this node
21308         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21309             return false;
21310         }
21311         // then bubble it up to the tree if the event wasn't cancelled
21312         var ot = this.getOwnerTree();
21313         if(ot){
21314             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21315                 return false;
21316             }
21317         }
21318         return true;
21319     },
21320
21321     /**
21322      * Returns true if this node is a leaf
21323      * @return {Boolean}
21324      */
21325     isLeaf : function(){
21326         return this.leaf === true;
21327     },
21328
21329     // private
21330     setFirstChild : function(node){
21331         this.firstChild = node;
21332     },
21333
21334     //private
21335     setLastChild : function(node){
21336         this.lastChild = node;
21337     },
21338
21339
21340     /**
21341      * Returns true if this node is the last child of its parent
21342      * @return {Boolean}
21343      */
21344     isLast : function(){
21345        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21346     },
21347
21348     /**
21349      * Returns true if this node is the first child of its parent
21350      * @return {Boolean}
21351      */
21352     isFirst : function(){
21353        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21354     },
21355
21356     hasChildNodes : function(){
21357         return !this.isLeaf() && this.childNodes.length > 0;
21358     },
21359
21360     /**
21361      * Insert node(s) as the last child node of this node.
21362      * @param {Node/Array} node The node or Array of nodes to append
21363      * @return {Node} The appended node if single append, or null if an array was passed
21364      */
21365     appendChild : function(node){
21366         var multi = false;
21367         if(node instanceof Array){
21368             multi = node;
21369         }else if(arguments.length > 1){
21370             multi = arguments;
21371         }
21372         // if passed an array or multiple args do them one by one
21373         if(multi){
21374             for(var i = 0, len = multi.length; i < len; i++) {
21375                 this.appendChild(multi[i]);
21376             }
21377         }else{
21378             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21379                 return false;
21380             }
21381             var index = this.childNodes.length;
21382             var oldParent = node.parentNode;
21383             // it's a move, make sure we move it cleanly
21384             if(oldParent){
21385                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21386                     return false;
21387                 }
21388                 oldParent.removeChild(node);
21389             }
21390             index = this.childNodes.length;
21391             if(index == 0){
21392                 this.setFirstChild(node);
21393             }
21394             this.childNodes.push(node);
21395             node.parentNode = this;
21396             var ps = this.childNodes[index-1];
21397             if(ps){
21398                 node.previousSibling = ps;
21399                 ps.nextSibling = node;
21400             }else{
21401                 node.previousSibling = null;
21402             }
21403             node.nextSibling = null;
21404             this.setLastChild(node);
21405             node.setOwnerTree(this.getOwnerTree());
21406             this.fireEvent("append", this.ownerTree, this, node, index);
21407             if(oldParent){
21408                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21409             }
21410             return node;
21411         }
21412     },
21413
21414     /**
21415      * Removes a child node from this node.
21416      * @param {Node} node The node to remove
21417      * @return {Node} The removed node
21418      */
21419     removeChild : function(node){
21420         var index = this.childNodes.indexOf(node);
21421         if(index == -1){
21422             return false;
21423         }
21424         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21425             return false;
21426         }
21427
21428         // remove it from childNodes collection
21429         this.childNodes.splice(index, 1);
21430
21431         // update siblings
21432         if(node.previousSibling){
21433             node.previousSibling.nextSibling = node.nextSibling;
21434         }
21435         if(node.nextSibling){
21436             node.nextSibling.previousSibling = node.previousSibling;
21437         }
21438
21439         // update child refs
21440         if(this.firstChild == node){
21441             this.setFirstChild(node.nextSibling);
21442         }
21443         if(this.lastChild == node){
21444             this.setLastChild(node.previousSibling);
21445         }
21446
21447         node.setOwnerTree(null);
21448         // clear any references from the node
21449         node.parentNode = null;
21450         node.previousSibling = null;
21451         node.nextSibling = null;
21452         this.fireEvent("remove", this.ownerTree, this, node);
21453         return node;
21454     },
21455
21456     /**
21457      * Inserts the first node before the second node in this nodes childNodes collection.
21458      * @param {Node} node The node to insert
21459      * @param {Node} refNode The node to insert before (if null the node is appended)
21460      * @return {Node} The inserted node
21461      */
21462     insertBefore : function(node, refNode){
21463         if(!refNode){ // like standard Dom, refNode can be null for append
21464             return this.appendChild(node);
21465         }
21466         // nothing to do
21467         if(node == refNode){
21468             return false;
21469         }
21470
21471         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21472             return false;
21473         }
21474         var index = this.childNodes.indexOf(refNode);
21475         var oldParent = node.parentNode;
21476         var refIndex = index;
21477
21478         // when moving internally, indexes will change after remove
21479         if(oldParent == this && this.childNodes.indexOf(node) < index){
21480             refIndex--;
21481         }
21482
21483         // it's a move, make sure we move it cleanly
21484         if(oldParent){
21485             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21486                 return false;
21487             }
21488             oldParent.removeChild(node);
21489         }
21490         if(refIndex == 0){
21491             this.setFirstChild(node);
21492         }
21493         this.childNodes.splice(refIndex, 0, node);
21494         node.parentNode = this;
21495         var ps = this.childNodes[refIndex-1];
21496         if(ps){
21497             node.previousSibling = ps;
21498             ps.nextSibling = node;
21499         }else{
21500             node.previousSibling = null;
21501         }
21502         node.nextSibling = refNode;
21503         refNode.previousSibling = node;
21504         node.setOwnerTree(this.getOwnerTree());
21505         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21506         if(oldParent){
21507             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21508         }
21509         return node;
21510     },
21511
21512     /**
21513      * Returns the child node at the specified index.
21514      * @param {Number} index
21515      * @return {Node}
21516      */
21517     item : function(index){
21518         return this.childNodes[index];
21519     },
21520
21521     /**
21522      * Replaces one child node in this node with another.
21523      * @param {Node} newChild The replacement node
21524      * @param {Node} oldChild The node to replace
21525      * @return {Node} The replaced node
21526      */
21527     replaceChild : function(newChild, oldChild){
21528         this.insertBefore(newChild, oldChild);
21529         this.removeChild(oldChild);
21530         return oldChild;
21531     },
21532
21533     /**
21534      * Returns the index of a child node
21535      * @param {Node} node
21536      * @return {Number} The index of the node or -1 if it was not found
21537      */
21538     indexOf : function(child){
21539         return this.childNodes.indexOf(child);
21540     },
21541
21542     /**
21543      * Returns the tree this node is in.
21544      * @return {Tree}
21545      */
21546     getOwnerTree : function(){
21547         // if it doesn't have one, look for one
21548         if(!this.ownerTree){
21549             var p = this;
21550             while(p){
21551                 if(p.ownerTree){
21552                     this.ownerTree = p.ownerTree;
21553                     break;
21554                 }
21555                 p = p.parentNode;
21556             }
21557         }
21558         return this.ownerTree;
21559     },
21560
21561     /**
21562      * Returns depth of this node (the root node has a depth of 0)
21563      * @return {Number}
21564      */
21565     getDepth : function(){
21566         var depth = 0;
21567         var p = this;
21568         while(p.parentNode){
21569             ++depth;
21570             p = p.parentNode;
21571         }
21572         return depth;
21573     },
21574
21575     // private
21576     setOwnerTree : function(tree){
21577         // if it's move, we need to update everyone
21578         if(tree != this.ownerTree){
21579             if(this.ownerTree){
21580                 this.ownerTree.unregisterNode(this);
21581             }
21582             this.ownerTree = tree;
21583             var cs = this.childNodes;
21584             for(var i = 0, len = cs.length; i < len; i++) {
21585                 cs[i].setOwnerTree(tree);
21586             }
21587             if(tree){
21588                 tree.registerNode(this);
21589             }
21590         }
21591     },
21592
21593     /**
21594      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21595      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21596      * @return {String} The path
21597      */
21598     getPath : function(attr){
21599         attr = attr || "id";
21600         var p = this.parentNode;
21601         var b = [this.attributes[attr]];
21602         while(p){
21603             b.unshift(p.attributes[attr]);
21604             p = p.parentNode;
21605         }
21606         var sep = this.getOwnerTree().pathSeparator;
21607         return sep + b.join(sep);
21608     },
21609
21610     /**
21611      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21612      * function call will be the scope provided or the current node. The arguments to the function
21613      * will be the args provided or the current node. If the function returns false at any point,
21614      * the bubble is stopped.
21615      * @param {Function} fn The function to call
21616      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21617      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21618      */
21619     bubble : function(fn, scope, args){
21620         var p = this;
21621         while(p){
21622             if(fn.call(scope || p, args || p) === false){
21623                 break;
21624             }
21625             p = p.parentNode;
21626         }
21627     },
21628
21629     /**
21630      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21631      * function call will be the scope provided or the current node. The arguments to the function
21632      * will be the args provided or the current node. If the function returns false at any point,
21633      * the cascade is stopped on that branch.
21634      * @param {Function} fn The function to call
21635      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21636      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21637      */
21638     cascade : function(fn, scope, args){
21639         if(fn.call(scope || this, args || this) !== false){
21640             var cs = this.childNodes;
21641             for(var i = 0, len = cs.length; i < len; i++) {
21642                 cs[i].cascade(fn, scope, args);
21643             }
21644         }
21645     },
21646
21647     /**
21648      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21649      * function call will be the scope provided or the current node. The arguments to the function
21650      * will be the args provided or the current node. If the function returns false at any point,
21651      * the iteration stops.
21652      * @param {Function} fn The function to call
21653      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21654      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21655      */
21656     eachChild : function(fn, scope, args){
21657         var cs = this.childNodes;
21658         for(var i = 0, len = cs.length; i < len; i++) {
21659                 if(fn.call(scope || this, args || cs[i]) === false){
21660                     break;
21661                 }
21662         }
21663     },
21664
21665     /**
21666      * Finds the first child that has the attribute with the specified value.
21667      * @param {String} attribute The attribute name
21668      * @param {Mixed} value The value to search for
21669      * @return {Node} The found child or null if none was found
21670      */
21671     findChild : function(attribute, value){
21672         var cs = this.childNodes;
21673         for(var i = 0, len = cs.length; i < len; i++) {
21674                 if(cs[i].attributes[attribute] == value){
21675                     return cs[i];
21676                 }
21677         }
21678         return null;
21679     },
21680
21681     /**
21682      * Finds the first child by a custom function. The child matches if the function passed
21683      * returns true.
21684      * @param {Function} fn
21685      * @param {Object} scope (optional)
21686      * @return {Node} The found child or null if none was found
21687      */
21688     findChildBy : function(fn, scope){
21689         var cs = this.childNodes;
21690         for(var i = 0, len = cs.length; i < len; i++) {
21691                 if(fn.call(scope||cs[i], cs[i]) === true){
21692                     return cs[i];
21693                 }
21694         }
21695         return null;
21696     },
21697
21698     /**
21699      * Sorts this nodes children using the supplied sort function
21700      * @param {Function} fn
21701      * @param {Object} scope (optional)
21702      */
21703     sort : function(fn, scope){
21704         var cs = this.childNodes;
21705         var len = cs.length;
21706         if(len > 0){
21707             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21708             cs.sort(sortFn);
21709             for(var i = 0; i < len; i++){
21710                 var n = cs[i];
21711                 n.previousSibling = cs[i-1];
21712                 n.nextSibling = cs[i+1];
21713                 if(i == 0){
21714                     this.setFirstChild(n);
21715                 }
21716                 if(i == len-1){
21717                     this.setLastChild(n);
21718                 }
21719             }
21720         }
21721     },
21722
21723     /**
21724      * Returns true if this node is an ancestor (at any point) of the passed node.
21725      * @param {Node} node
21726      * @return {Boolean}
21727      */
21728     contains : function(node){
21729         return node.isAncestor(this);
21730     },
21731
21732     /**
21733      * Returns true if the passed node is an ancestor (at any point) of this node.
21734      * @param {Node} node
21735      * @return {Boolean}
21736      */
21737     isAncestor : function(node){
21738         var p = this.parentNode;
21739         while(p){
21740             if(p == node){
21741                 return true;
21742             }
21743             p = p.parentNode;
21744         }
21745         return false;
21746     },
21747
21748     toString : function(){
21749         return "[Node"+(this.id?" "+this.id:"")+"]";
21750     }
21751 });/*
21752  * Based on:
21753  * Ext JS Library 1.1.1
21754  * Copyright(c) 2006-2007, Ext JS, LLC.
21755  *
21756  * Originally Released Under LGPL - original licence link has changed is not relivant.
21757  *
21758  * Fork - LGPL
21759  * <script type="text/javascript">
21760  */
21761  
21762
21763 /**
21764  * @class Roo.ComponentMgr
21765  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21766  * @singleton
21767  */
21768 Roo.ComponentMgr = function(){
21769     var all = new Roo.util.MixedCollection();
21770
21771     return {
21772         /**
21773          * Registers a component.
21774          * @param {Roo.Component} c The component
21775          */
21776         register : function(c){
21777             all.add(c);
21778         },
21779
21780         /**
21781          * Unregisters a component.
21782          * @param {Roo.Component} c The component
21783          */
21784         unregister : function(c){
21785             all.remove(c);
21786         },
21787
21788         /**
21789          * Returns a component by id
21790          * @param {String} id The component id
21791          */
21792         get : function(id){
21793             return all.get(id);
21794         },
21795
21796         /**
21797          * Registers a function that will be called when a specified component is added to ComponentMgr
21798          * @param {String} id The component id
21799          * @param {Funtction} fn The callback function
21800          * @param {Object} scope The scope of the callback
21801          */
21802         onAvailable : function(id, fn, scope){
21803             all.on("add", function(index, o){
21804                 if(o.id == id){
21805                     fn.call(scope || o, o);
21806                     all.un("add", fn, scope);
21807                 }
21808             });
21809         }
21810     };
21811 }();/*
21812  * Based on:
21813  * Ext JS Library 1.1.1
21814  * Copyright(c) 2006-2007, Ext JS, LLC.
21815  *
21816  * Originally Released Under LGPL - original licence link has changed is not relivant.
21817  *
21818  * Fork - LGPL
21819  * <script type="text/javascript">
21820  */
21821  
21822 /**
21823  * @class Roo.Component
21824  * @extends Roo.util.Observable
21825  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21826  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21827  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21828  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21829  * All visual components (widgets) that require rendering into a layout should subclass Component.
21830  * @constructor
21831  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21832  * 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
21833  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21834  */
21835 Roo.Component = function(config){
21836     config = config || {};
21837     if(config.tagName || config.dom || typeof config == "string"){ // element object
21838         config = {el: config, id: config.id || config};
21839     }
21840     this.initialConfig = config;
21841
21842     Roo.apply(this, config);
21843     this.addEvents({
21844         /**
21845          * @event disable
21846          * Fires after the component is disabled.
21847              * @param {Roo.Component} this
21848              */
21849         disable : true,
21850         /**
21851          * @event enable
21852          * Fires after the component is enabled.
21853              * @param {Roo.Component} this
21854              */
21855         enable : true,
21856         /**
21857          * @event beforeshow
21858          * Fires before the component is shown.  Return false to stop the show.
21859              * @param {Roo.Component} this
21860              */
21861         beforeshow : true,
21862         /**
21863          * @event show
21864          * Fires after the component is shown.
21865              * @param {Roo.Component} this
21866              */
21867         show : true,
21868         /**
21869          * @event beforehide
21870          * Fires before the component is hidden. Return false to stop the hide.
21871              * @param {Roo.Component} this
21872              */
21873         beforehide : true,
21874         /**
21875          * @event hide
21876          * Fires after the component is hidden.
21877              * @param {Roo.Component} this
21878              */
21879         hide : true,
21880         /**
21881          * @event beforerender
21882          * Fires before the component is rendered. Return false to stop the render.
21883              * @param {Roo.Component} this
21884              */
21885         beforerender : true,
21886         /**
21887          * @event render
21888          * Fires after the component is rendered.
21889              * @param {Roo.Component} this
21890              */
21891         render : true,
21892         /**
21893          * @event beforedestroy
21894          * Fires before the component is destroyed. Return false to stop the destroy.
21895              * @param {Roo.Component} this
21896              */
21897         beforedestroy : true,
21898         /**
21899          * @event destroy
21900          * Fires after the component is destroyed.
21901              * @param {Roo.Component} this
21902              */
21903         destroy : true
21904     });
21905     if(!this.id){
21906         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21907     }
21908     Roo.ComponentMgr.register(this);
21909     Roo.Component.superclass.constructor.call(this);
21910     this.initComponent();
21911     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21912         this.render(this.renderTo);
21913         delete this.renderTo;
21914     }
21915 };
21916
21917 /** @private */
21918 Roo.Component.AUTO_ID = 1000;
21919
21920 Roo.extend(Roo.Component, Roo.util.Observable, {
21921     /**
21922      * @scope Roo.Component.prototype
21923      * @type {Boolean}
21924      * true if this component is hidden. Read-only.
21925      */
21926     hidden : false,
21927     /**
21928      * @type {Boolean}
21929      * true if this component is disabled. Read-only.
21930      */
21931     disabled : false,
21932     /**
21933      * @type {Boolean}
21934      * true if this component has been rendered. Read-only.
21935      */
21936     rendered : false,
21937     
21938     /** @cfg {String} disableClass
21939      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21940      */
21941     disabledClass : "x-item-disabled",
21942         /** @cfg {Boolean} allowDomMove
21943          * Whether the component can move the Dom node when rendering (defaults to true).
21944          */
21945     allowDomMove : true,
21946     /** @cfg {String} hideMode
21947      * How this component should hidden. Supported values are
21948      * "visibility" (css visibility), "offsets" (negative offset position) and
21949      * "display" (css display) - defaults to "display".
21950      */
21951     hideMode: 'display',
21952
21953     /** @private */
21954     ctype : "Roo.Component",
21955
21956     /**
21957      * @cfg {String} actionMode 
21958      * which property holds the element that used for  hide() / show() / disable() / enable()
21959      * default is 'el' 
21960      */
21961     actionMode : "el",
21962
21963     /** @private */
21964     getActionEl : function(){
21965         return this[this.actionMode];
21966     },
21967
21968     initComponent : Roo.emptyFn,
21969     /**
21970      * If this is a lazy rendering component, render it to its container element.
21971      * @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.
21972      */
21973     render : function(container, position){
21974         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21975             if(!container && this.el){
21976                 this.el = Roo.get(this.el);
21977                 container = this.el.dom.parentNode;
21978                 this.allowDomMove = false;
21979             }
21980             this.container = Roo.get(container);
21981             this.rendered = true;
21982             if(position !== undefined){
21983                 if(typeof position == 'number'){
21984                     position = this.container.dom.childNodes[position];
21985                 }else{
21986                     position = Roo.getDom(position);
21987                 }
21988             }
21989             this.onRender(this.container, position || null);
21990             if(this.cls){
21991                 this.el.addClass(this.cls);
21992                 delete this.cls;
21993             }
21994             if(this.style){
21995                 this.el.applyStyles(this.style);
21996                 delete this.style;
21997             }
21998             this.fireEvent("render", this);
21999             this.afterRender(this.container);
22000             if(this.hidden){
22001                 this.hide();
22002             }
22003             if(this.disabled){
22004                 this.disable();
22005             }
22006         }
22007         return this;
22008     },
22009
22010     /** @private */
22011     // default function is not really useful
22012     onRender : function(ct, position){
22013         if(this.el){
22014             this.el = Roo.get(this.el);
22015             if(this.allowDomMove !== false){
22016                 ct.dom.insertBefore(this.el.dom, position);
22017             }
22018         }
22019     },
22020
22021     /** @private */
22022     getAutoCreate : function(){
22023         var cfg = typeof this.autoCreate == "object" ?
22024                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22025         if(this.id && !cfg.id){
22026             cfg.id = this.id;
22027         }
22028         return cfg;
22029     },
22030
22031     /** @private */
22032     afterRender : Roo.emptyFn,
22033
22034     /**
22035      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22036      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22037      */
22038     destroy : function(){
22039         if(this.fireEvent("beforedestroy", this) !== false){
22040             this.purgeListeners();
22041             this.beforeDestroy();
22042             if(this.rendered){
22043                 this.el.removeAllListeners();
22044                 this.el.remove();
22045                 if(this.actionMode == "container"){
22046                     this.container.remove();
22047                 }
22048             }
22049             this.onDestroy();
22050             Roo.ComponentMgr.unregister(this);
22051             this.fireEvent("destroy", this);
22052         }
22053     },
22054
22055         /** @private */
22056     beforeDestroy : function(){
22057
22058     },
22059
22060         /** @private */
22061         onDestroy : function(){
22062
22063     },
22064
22065     /**
22066      * Returns the underlying {@link Roo.Element}.
22067      * @return {Roo.Element} The element
22068      */
22069     getEl : function(){
22070         return this.el;
22071     },
22072
22073     /**
22074      * Returns the id of this component.
22075      * @return {String}
22076      */
22077     getId : function(){
22078         return this.id;
22079     },
22080
22081     /**
22082      * Try to focus this component.
22083      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22084      * @return {Roo.Component} this
22085      */
22086     focus : function(selectText){
22087         if(this.rendered){
22088             this.el.focus();
22089             if(selectText === true){
22090                 this.el.dom.select();
22091             }
22092         }
22093         return this;
22094     },
22095
22096     /** @private */
22097     blur : function(){
22098         if(this.rendered){
22099             this.el.blur();
22100         }
22101         return this;
22102     },
22103
22104     /**
22105      * Disable this component.
22106      * @return {Roo.Component} this
22107      */
22108     disable : function(){
22109         if(this.rendered){
22110             this.onDisable();
22111         }
22112         this.disabled = true;
22113         this.fireEvent("disable", this);
22114         return this;
22115     },
22116
22117         // private
22118     onDisable : function(){
22119         this.getActionEl().addClass(this.disabledClass);
22120         this.el.dom.disabled = true;
22121     },
22122
22123     /**
22124      * Enable this component.
22125      * @return {Roo.Component} this
22126      */
22127     enable : function(){
22128         if(this.rendered){
22129             this.onEnable();
22130         }
22131         this.disabled = false;
22132         this.fireEvent("enable", this);
22133         return this;
22134     },
22135
22136         // private
22137     onEnable : function(){
22138         this.getActionEl().removeClass(this.disabledClass);
22139         this.el.dom.disabled = false;
22140     },
22141
22142     /**
22143      * Convenience function for setting disabled/enabled by boolean.
22144      * @param {Boolean} disabled
22145      */
22146     setDisabled : function(disabled){
22147         this[disabled ? "disable" : "enable"]();
22148     },
22149
22150     /**
22151      * Show this component.
22152      * @return {Roo.Component} this
22153      */
22154     show: function(){
22155         if(this.fireEvent("beforeshow", this) !== false){
22156             this.hidden = false;
22157             if(this.rendered){
22158                 this.onShow();
22159             }
22160             this.fireEvent("show", this);
22161         }
22162         return this;
22163     },
22164
22165     // private
22166     onShow : function(){
22167         var ae = this.getActionEl();
22168         if(this.hideMode == 'visibility'){
22169             ae.dom.style.visibility = "visible";
22170         }else if(this.hideMode == 'offsets'){
22171             ae.removeClass('x-hidden');
22172         }else{
22173             ae.dom.style.display = "";
22174         }
22175     },
22176
22177     /**
22178      * Hide this component.
22179      * @return {Roo.Component} this
22180      */
22181     hide: function(){
22182         if(this.fireEvent("beforehide", this) !== false){
22183             this.hidden = true;
22184             if(this.rendered){
22185                 this.onHide();
22186             }
22187             this.fireEvent("hide", this);
22188         }
22189         return this;
22190     },
22191
22192     // private
22193     onHide : function(){
22194         var ae = this.getActionEl();
22195         if(this.hideMode == 'visibility'){
22196             ae.dom.style.visibility = "hidden";
22197         }else if(this.hideMode == 'offsets'){
22198             ae.addClass('x-hidden');
22199         }else{
22200             ae.dom.style.display = "none";
22201         }
22202     },
22203
22204     /**
22205      * Convenience function to hide or show this component by boolean.
22206      * @param {Boolean} visible True to show, false to hide
22207      * @return {Roo.Component} this
22208      */
22209     setVisible: function(visible){
22210         if(visible) {
22211             this.show();
22212         }else{
22213             this.hide();
22214         }
22215         return this;
22216     },
22217
22218     /**
22219      * Returns true if this component is visible.
22220      */
22221     isVisible : function(){
22222         return this.getActionEl().isVisible();
22223     },
22224
22225     cloneConfig : function(overrides){
22226         overrides = overrides || {};
22227         var id = overrides.id || Roo.id();
22228         var cfg = Roo.applyIf(overrides, this.initialConfig);
22229         cfg.id = id; // prevent dup id
22230         return new this.constructor(cfg);
22231     }
22232 });/*
22233  * Based on:
22234  * Ext JS Library 1.1.1
22235  * Copyright(c) 2006-2007, Ext JS, LLC.
22236  *
22237  * Originally Released Under LGPL - original licence link has changed is not relivant.
22238  *
22239  * Fork - LGPL
22240  * <script type="text/javascript">
22241  */
22242  (function(){ 
22243 /**
22244  * @class Roo.Layer
22245  * @extends Roo.Element
22246  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22247  * automatic maintaining of shadow/shim positions.
22248  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22249  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22250  * you can pass a string with a CSS class name. False turns off the shadow.
22251  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22252  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22253  * @cfg {String} cls CSS class to add to the element
22254  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22255  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22256  * @constructor
22257  * @param {Object} config An object with config options.
22258  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22259  */
22260
22261 Roo.Layer = function(config, existingEl){
22262     config = config || {};
22263     var dh = Roo.DomHelper;
22264     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22265     if(existingEl){
22266         this.dom = Roo.getDom(existingEl);
22267     }
22268     if(!this.dom){
22269         var o = config.dh || {tag: "div", cls: "x-layer"};
22270         this.dom = dh.append(pel, o);
22271     }
22272     if(config.cls){
22273         this.addClass(config.cls);
22274     }
22275     this.constrain = config.constrain !== false;
22276     this.visibilityMode = Roo.Element.VISIBILITY;
22277     if(config.id){
22278         this.id = this.dom.id = config.id;
22279     }else{
22280         this.id = Roo.id(this.dom);
22281     }
22282     this.zindex = config.zindex || this.getZIndex();
22283     this.position("absolute", this.zindex);
22284     if(config.shadow){
22285         this.shadowOffset = config.shadowOffset || 4;
22286         this.shadow = new Roo.Shadow({
22287             offset : this.shadowOffset,
22288             mode : config.shadow
22289         });
22290     }else{
22291         this.shadowOffset = 0;
22292     }
22293     this.useShim = config.shim !== false && Roo.useShims;
22294     this.useDisplay = config.useDisplay;
22295     this.hide();
22296 };
22297
22298 var supr = Roo.Element.prototype;
22299
22300 // shims are shared among layer to keep from having 100 iframes
22301 var shims = [];
22302
22303 Roo.extend(Roo.Layer, Roo.Element, {
22304
22305     getZIndex : function(){
22306         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22307     },
22308
22309     getShim : function(){
22310         if(!this.useShim){
22311             return null;
22312         }
22313         if(this.shim){
22314             return this.shim;
22315         }
22316         var shim = shims.shift();
22317         if(!shim){
22318             shim = this.createShim();
22319             shim.enableDisplayMode('block');
22320             shim.dom.style.display = 'none';
22321             shim.dom.style.visibility = 'visible';
22322         }
22323         var pn = this.dom.parentNode;
22324         if(shim.dom.parentNode != pn){
22325             pn.insertBefore(shim.dom, this.dom);
22326         }
22327         shim.setStyle('z-index', this.getZIndex()-2);
22328         this.shim = shim;
22329         return shim;
22330     },
22331
22332     hideShim : function(){
22333         if(this.shim){
22334             this.shim.setDisplayed(false);
22335             shims.push(this.shim);
22336             delete this.shim;
22337         }
22338     },
22339
22340     disableShadow : function(){
22341         if(this.shadow){
22342             this.shadowDisabled = true;
22343             this.shadow.hide();
22344             this.lastShadowOffset = this.shadowOffset;
22345             this.shadowOffset = 0;
22346         }
22347     },
22348
22349     enableShadow : function(show){
22350         if(this.shadow){
22351             this.shadowDisabled = false;
22352             this.shadowOffset = this.lastShadowOffset;
22353             delete this.lastShadowOffset;
22354             if(show){
22355                 this.sync(true);
22356             }
22357         }
22358     },
22359
22360     // private
22361     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22362     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22363     sync : function(doShow){
22364         var sw = this.shadow;
22365         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22366             var sh = this.getShim();
22367
22368             var w = this.getWidth(),
22369                 h = this.getHeight();
22370
22371             var l = this.getLeft(true),
22372                 t = this.getTop(true);
22373
22374             if(sw && !this.shadowDisabled){
22375                 if(doShow && !sw.isVisible()){
22376                     sw.show(this);
22377                 }else{
22378                     sw.realign(l, t, w, h);
22379                 }
22380                 if(sh){
22381                     if(doShow){
22382                        sh.show();
22383                     }
22384                     // fit the shim behind the shadow, so it is shimmed too
22385                     var a = sw.adjusts, s = sh.dom.style;
22386                     s.left = (Math.min(l, l+a.l))+"px";
22387                     s.top = (Math.min(t, t+a.t))+"px";
22388                     s.width = (w+a.w)+"px";
22389                     s.height = (h+a.h)+"px";
22390                 }
22391             }else if(sh){
22392                 if(doShow){
22393                    sh.show();
22394                 }
22395                 sh.setSize(w, h);
22396                 sh.setLeftTop(l, t);
22397             }
22398             
22399         }
22400     },
22401
22402     // private
22403     destroy : function(){
22404         this.hideShim();
22405         if(this.shadow){
22406             this.shadow.hide();
22407         }
22408         this.removeAllListeners();
22409         var pn = this.dom.parentNode;
22410         if(pn){
22411             pn.removeChild(this.dom);
22412         }
22413         Roo.Element.uncache(this.id);
22414     },
22415
22416     remove : function(){
22417         this.destroy();
22418     },
22419
22420     // private
22421     beginUpdate : function(){
22422         this.updating = true;
22423     },
22424
22425     // private
22426     endUpdate : function(){
22427         this.updating = false;
22428         this.sync(true);
22429     },
22430
22431     // private
22432     hideUnders : function(negOffset){
22433         if(this.shadow){
22434             this.shadow.hide();
22435         }
22436         this.hideShim();
22437     },
22438
22439     // private
22440     constrainXY : function(){
22441         if(this.constrain){
22442             var vw = Roo.lib.Dom.getViewWidth(),
22443                 vh = Roo.lib.Dom.getViewHeight();
22444             var s = Roo.get(document).getScroll();
22445
22446             var xy = this.getXY();
22447             var x = xy[0], y = xy[1];   
22448             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22449             // only move it if it needs it
22450             var moved = false;
22451             // first validate right/bottom
22452             if((x + w) > vw+s.left){
22453                 x = vw - w - this.shadowOffset;
22454                 moved = true;
22455             }
22456             if((y + h) > vh+s.top){
22457                 y = vh - h - this.shadowOffset;
22458                 moved = true;
22459             }
22460             // then make sure top/left isn't negative
22461             if(x < s.left){
22462                 x = s.left;
22463                 moved = true;
22464             }
22465             if(y < s.top){
22466                 y = s.top;
22467                 moved = true;
22468             }
22469             if(moved){
22470                 if(this.avoidY){
22471                     var ay = this.avoidY;
22472                     if(y <= ay && (y+h) >= ay){
22473                         y = ay-h-5;   
22474                     }
22475                 }
22476                 xy = [x, y];
22477                 this.storeXY(xy);
22478                 supr.setXY.call(this, xy);
22479                 this.sync();
22480             }
22481         }
22482     },
22483
22484     isVisible : function(){
22485         return this.visible;    
22486     },
22487
22488     // private
22489     showAction : function(){
22490         this.visible = true; // track visibility to prevent getStyle calls
22491         if(this.useDisplay === true){
22492             this.setDisplayed("");
22493         }else if(this.lastXY){
22494             supr.setXY.call(this, this.lastXY);
22495         }else if(this.lastLT){
22496             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22497         }
22498     },
22499
22500     // private
22501     hideAction : function(){
22502         this.visible = false;
22503         if(this.useDisplay === true){
22504             this.setDisplayed(false);
22505         }else{
22506             this.setLeftTop(-10000,-10000);
22507         }
22508     },
22509
22510     // overridden Element method
22511     setVisible : function(v, a, d, c, e){
22512         if(v){
22513             this.showAction();
22514         }
22515         if(a && v){
22516             var cb = function(){
22517                 this.sync(true);
22518                 if(c){
22519                     c();
22520                 }
22521             }.createDelegate(this);
22522             supr.setVisible.call(this, true, true, d, cb, e);
22523         }else{
22524             if(!v){
22525                 this.hideUnders(true);
22526             }
22527             var cb = c;
22528             if(a){
22529                 cb = function(){
22530                     this.hideAction();
22531                     if(c){
22532                         c();
22533                     }
22534                 }.createDelegate(this);
22535             }
22536             supr.setVisible.call(this, v, a, d, cb, e);
22537             if(v){
22538                 this.sync(true);
22539             }else if(!a){
22540                 this.hideAction();
22541             }
22542         }
22543     },
22544
22545     storeXY : function(xy){
22546         delete this.lastLT;
22547         this.lastXY = xy;
22548     },
22549
22550     storeLeftTop : function(left, top){
22551         delete this.lastXY;
22552         this.lastLT = [left, top];
22553     },
22554
22555     // private
22556     beforeFx : function(){
22557         this.beforeAction();
22558         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22559     },
22560
22561     // private
22562     afterFx : function(){
22563         Roo.Layer.superclass.afterFx.apply(this, arguments);
22564         this.sync(this.isVisible());
22565     },
22566
22567     // private
22568     beforeAction : function(){
22569         if(!this.updating && this.shadow){
22570             this.shadow.hide();
22571         }
22572     },
22573
22574     // overridden Element method
22575     setLeft : function(left){
22576         this.storeLeftTop(left, this.getTop(true));
22577         supr.setLeft.apply(this, arguments);
22578         this.sync();
22579     },
22580
22581     setTop : function(top){
22582         this.storeLeftTop(this.getLeft(true), top);
22583         supr.setTop.apply(this, arguments);
22584         this.sync();
22585     },
22586
22587     setLeftTop : function(left, top){
22588         this.storeLeftTop(left, top);
22589         supr.setLeftTop.apply(this, arguments);
22590         this.sync();
22591     },
22592
22593     setXY : function(xy, a, d, c, e){
22594         this.fixDisplay();
22595         this.beforeAction();
22596         this.storeXY(xy);
22597         var cb = this.createCB(c);
22598         supr.setXY.call(this, xy, a, d, cb, e);
22599         if(!a){
22600             cb();
22601         }
22602     },
22603
22604     // private
22605     createCB : function(c){
22606         var el = this;
22607         return function(){
22608             el.constrainXY();
22609             el.sync(true);
22610             if(c){
22611                 c();
22612             }
22613         };
22614     },
22615
22616     // overridden Element method
22617     setX : function(x, a, d, c, e){
22618         this.setXY([x, this.getY()], a, d, c, e);
22619     },
22620
22621     // overridden Element method
22622     setY : function(y, a, d, c, e){
22623         this.setXY([this.getX(), y], a, d, c, e);
22624     },
22625
22626     // overridden Element method
22627     setSize : function(w, h, a, d, c, e){
22628         this.beforeAction();
22629         var cb = this.createCB(c);
22630         supr.setSize.call(this, w, h, a, d, cb, e);
22631         if(!a){
22632             cb();
22633         }
22634     },
22635
22636     // overridden Element method
22637     setWidth : function(w, a, d, c, e){
22638         this.beforeAction();
22639         var cb = this.createCB(c);
22640         supr.setWidth.call(this, w, a, d, cb, e);
22641         if(!a){
22642             cb();
22643         }
22644     },
22645
22646     // overridden Element method
22647     setHeight : function(h, a, d, c, e){
22648         this.beforeAction();
22649         var cb = this.createCB(c);
22650         supr.setHeight.call(this, h, a, d, cb, e);
22651         if(!a){
22652             cb();
22653         }
22654     },
22655
22656     // overridden Element method
22657     setBounds : function(x, y, w, h, a, d, c, e){
22658         this.beforeAction();
22659         var cb = this.createCB(c);
22660         if(!a){
22661             this.storeXY([x, y]);
22662             supr.setXY.call(this, [x, y]);
22663             supr.setSize.call(this, w, h, a, d, cb, e);
22664             cb();
22665         }else{
22666             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22667         }
22668         return this;
22669     },
22670     
22671     /**
22672      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22673      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22674      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22675      * @param {Number} zindex The new z-index to set
22676      * @return {this} The Layer
22677      */
22678     setZIndex : function(zindex){
22679         this.zindex = zindex;
22680         this.setStyle("z-index", zindex + 2);
22681         if(this.shadow){
22682             this.shadow.setZIndex(zindex + 1);
22683         }
22684         if(this.shim){
22685             this.shim.setStyle("z-index", zindex);
22686         }
22687     }
22688 });
22689 })();/*
22690  * Based on:
22691  * Ext JS Library 1.1.1
22692  * Copyright(c) 2006-2007, Ext JS, LLC.
22693  *
22694  * Originally Released Under LGPL - original licence link has changed is not relivant.
22695  *
22696  * Fork - LGPL
22697  * <script type="text/javascript">
22698  */
22699
22700
22701 /**
22702  * @class Roo.Shadow
22703  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22704  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22705  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22706  * @constructor
22707  * Create a new Shadow
22708  * @param {Object} config The config object
22709  */
22710 Roo.Shadow = function(config){
22711     Roo.apply(this, config);
22712     if(typeof this.mode != "string"){
22713         this.mode = this.defaultMode;
22714     }
22715     var o = this.offset, a = {h: 0};
22716     var rad = Math.floor(this.offset/2);
22717     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22718         case "drop":
22719             a.w = 0;
22720             a.l = a.t = o;
22721             a.t -= 1;
22722             if(Roo.isIE){
22723                 a.l -= this.offset + rad;
22724                 a.t -= this.offset + rad;
22725                 a.w -= rad;
22726                 a.h -= rad;
22727                 a.t += 1;
22728             }
22729         break;
22730         case "sides":
22731             a.w = (o*2);
22732             a.l = -o;
22733             a.t = o-1;
22734             if(Roo.isIE){
22735                 a.l -= (this.offset - rad);
22736                 a.t -= this.offset + rad;
22737                 a.l += 1;
22738                 a.w -= (this.offset - rad)*2;
22739                 a.w -= rad + 1;
22740                 a.h -= 1;
22741             }
22742         break;
22743         case "frame":
22744             a.w = a.h = (o*2);
22745             a.l = a.t = -o;
22746             a.t += 1;
22747             a.h -= 2;
22748             if(Roo.isIE){
22749                 a.l -= (this.offset - rad);
22750                 a.t -= (this.offset - rad);
22751                 a.l += 1;
22752                 a.w -= (this.offset + rad + 1);
22753                 a.h -= (this.offset + rad);
22754                 a.h += 1;
22755             }
22756         break;
22757     };
22758
22759     this.adjusts = a;
22760 };
22761
22762 Roo.Shadow.prototype = {
22763     /**
22764      * @cfg {String} mode
22765      * The shadow display mode.  Supports the following options:<br />
22766      * sides: Shadow displays on both sides and bottom only<br />
22767      * frame: Shadow displays equally on all four sides<br />
22768      * drop: Traditional bottom-right drop shadow (default)
22769      */
22770     /**
22771      * @cfg {String} offset
22772      * The number of pixels to offset the shadow from the element (defaults to 4)
22773      */
22774     offset: 4,
22775
22776     // private
22777     defaultMode: "drop",
22778
22779     /**
22780      * Displays the shadow under the target element
22781      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22782      */
22783     show : function(target){
22784         target = Roo.get(target);
22785         if(!this.el){
22786             this.el = Roo.Shadow.Pool.pull();
22787             if(this.el.dom.nextSibling != target.dom){
22788                 this.el.insertBefore(target);
22789             }
22790         }
22791         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22792         if(Roo.isIE){
22793             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22794         }
22795         this.realign(
22796             target.getLeft(true),
22797             target.getTop(true),
22798             target.getWidth(),
22799             target.getHeight()
22800         );
22801         this.el.dom.style.display = "block";
22802     },
22803
22804     /**
22805      * Returns true if the shadow is visible, else false
22806      */
22807     isVisible : function(){
22808         return this.el ? true : false;  
22809     },
22810
22811     /**
22812      * Direct alignment when values are already available. Show must be called at least once before
22813      * calling this method to ensure it is initialized.
22814      * @param {Number} left The target element left position
22815      * @param {Number} top The target element top position
22816      * @param {Number} width The target element width
22817      * @param {Number} height The target element height
22818      */
22819     realign : function(l, t, w, h){
22820         if(!this.el){
22821             return;
22822         }
22823         var a = this.adjusts, d = this.el.dom, s = d.style;
22824         var iea = 0;
22825         s.left = (l+a.l)+"px";
22826         s.top = (t+a.t)+"px";
22827         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22828  
22829         if(s.width != sws || s.height != shs){
22830             s.width = sws;
22831             s.height = shs;
22832             if(!Roo.isIE){
22833                 var cn = d.childNodes;
22834                 var sww = Math.max(0, (sw-12))+"px";
22835                 cn[0].childNodes[1].style.width = sww;
22836                 cn[1].childNodes[1].style.width = sww;
22837                 cn[2].childNodes[1].style.width = sww;
22838                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22839             }
22840         }
22841     },
22842
22843     /**
22844      * Hides this shadow
22845      */
22846     hide : function(){
22847         if(this.el){
22848             this.el.dom.style.display = "none";
22849             Roo.Shadow.Pool.push(this.el);
22850             delete this.el;
22851         }
22852     },
22853
22854     /**
22855      * Adjust the z-index of this shadow
22856      * @param {Number} zindex The new z-index
22857      */
22858     setZIndex : function(z){
22859         this.zIndex = z;
22860         if(this.el){
22861             this.el.setStyle("z-index", z);
22862         }
22863     }
22864 };
22865
22866 // Private utility class that manages the internal Shadow cache
22867 Roo.Shadow.Pool = function(){
22868     var p = [];
22869     var markup = Roo.isIE ?
22870                  '<div class="x-ie-shadow"></div>' :
22871                  '<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>';
22872     return {
22873         pull : function(){
22874             var sh = p.shift();
22875             if(!sh){
22876                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22877                 sh.autoBoxAdjust = false;
22878             }
22879             return sh;
22880         },
22881
22882         push : function(sh){
22883             p.push(sh);
22884         }
22885     };
22886 }();/*
22887  * Based on:
22888  * Ext JS Library 1.1.1
22889  * Copyright(c) 2006-2007, Ext JS, LLC.
22890  *
22891  * Originally Released Under LGPL - original licence link has changed is not relivant.
22892  *
22893  * Fork - LGPL
22894  * <script type="text/javascript">
22895  */
22896
22897 /**
22898  * @class Roo.BoxComponent
22899  * @extends Roo.Component
22900  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22901  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22902  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22903  * layout containers.
22904  * @constructor
22905  * @param {Roo.Element/String/Object} config The configuration options.
22906  */
22907 Roo.BoxComponent = function(config){
22908     Roo.Component.call(this, config);
22909     this.addEvents({
22910         /**
22911          * @event resize
22912          * Fires after the component is resized.
22913              * @param {Roo.Component} this
22914              * @param {Number} adjWidth The box-adjusted width that was set
22915              * @param {Number} adjHeight The box-adjusted height that was set
22916              * @param {Number} rawWidth The width that was originally specified
22917              * @param {Number} rawHeight The height that was originally specified
22918              */
22919         resize : true,
22920         /**
22921          * @event move
22922          * Fires after the component is moved.
22923              * @param {Roo.Component} this
22924              * @param {Number} x The new x position
22925              * @param {Number} y The new y position
22926              */
22927         move : true
22928     });
22929 };
22930
22931 Roo.extend(Roo.BoxComponent, Roo.Component, {
22932     // private, set in afterRender to signify that the component has been rendered
22933     boxReady : false,
22934     // private, used to defer height settings to subclasses
22935     deferHeight: false,
22936     /** @cfg {Number} width
22937      * width (optional) size of component
22938      */
22939      /** @cfg {Number} height
22940      * height (optional) size of component
22941      */
22942      
22943     /**
22944      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22945      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22946      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22947      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22948      * @return {Roo.BoxComponent} this
22949      */
22950     setSize : function(w, h){
22951         // support for standard size objects
22952         if(typeof w == 'object'){
22953             h = w.height;
22954             w = w.width;
22955         }
22956         // not rendered
22957         if(!this.boxReady){
22958             this.width = w;
22959             this.height = h;
22960             return this;
22961         }
22962
22963         // prevent recalcs when not needed
22964         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22965             return this;
22966         }
22967         this.lastSize = {width: w, height: h};
22968
22969         var adj = this.adjustSize(w, h);
22970         var aw = adj.width, ah = adj.height;
22971         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22972             var rz = this.getResizeEl();
22973             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22974                 rz.setSize(aw, ah);
22975             }else if(!this.deferHeight && ah !== undefined){
22976                 rz.setHeight(ah);
22977             }else if(aw !== undefined){
22978                 rz.setWidth(aw);
22979             }
22980             this.onResize(aw, ah, w, h);
22981             this.fireEvent('resize', this, aw, ah, w, h);
22982         }
22983         return this;
22984     },
22985
22986     /**
22987      * Gets the current size of the component's underlying element.
22988      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22989      */
22990     getSize : function(){
22991         return this.el.getSize();
22992     },
22993
22994     /**
22995      * Gets the current XY position of the component's underlying element.
22996      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22997      * @return {Array} The XY position of the element (e.g., [100, 200])
22998      */
22999     getPosition : function(local){
23000         if(local === true){
23001             return [this.el.getLeft(true), this.el.getTop(true)];
23002         }
23003         return this.xy || this.el.getXY();
23004     },
23005
23006     /**
23007      * Gets the current box measurements of the component's underlying element.
23008      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23009      * @returns {Object} box An object in the format {x, y, width, height}
23010      */
23011     getBox : function(local){
23012         var s = this.el.getSize();
23013         if(local){
23014             s.x = this.el.getLeft(true);
23015             s.y = this.el.getTop(true);
23016         }else{
23017             var xy = this.xy || this.el.getXY();
23018             s.x = xy[0];
23019             s.y = xy[1];
23020         }
23021         return s;
23022     },
23023
23024     /**
23025      * Sets the current box measurements of the component's underlying element.
23026      * @param {Object} box An object in the format {x, y, width, height}
23027      * @returns {Roo.BoxComponent} this
23028      */
23029     updateBox : function(box){
23030         this.setSize(box.width, box.height);
23031         this.setPagePosition(box.x, box.y);
23032         return this;
23033     },
23034
23035     // protected
23036     getResizeEl : function(){
23037         return this.resizeEl || this.el;
23038     },
23039
23040     // protected
23041     getPositionEl : function(){
23042         return this.positionEl || this.el;
23043     },
23044
23045     /**
23046      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23047      * This method fires the move event.
23048      * @param {Number} left The new left
23049      * @param {Number} top The new top
23050      * @returns {Roo.BoxComponent} this
23051      */
23052     setPosition : function(x, y){
23053         this.x = x;
23054         this.y = y;
23055         if(!this.boxReady){
23056             return this;
23057         }
23058         var adj = this.adjustPosition(x, y);
23059         var ax = adj.x, ay = adj.y;
23060
23061         var el = this.getPositionEl();
23062         if(ax !== undefined || ay !== undefined){
23063             if(ax !== undefined && ay !== undefined){
23064                 el.setLeftTop(ax, ay);
23065             }else if(ax !== undefined){
23066                 el.setLeft(ax);
23067             }else if(ay !== undefined){
23068                 el.setTop(ay);
23069             }
23070             this.onPosition(ax, ay);
23071             this.fireEvent('move', this, ax, ay);
23072         }
23073         return this;
23074     },
23075
23076     /**
23077      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23078      * This method fires the move event.
23079      * @param {Number} x The new x position
23080      * @param {Number} y The new y position
23081      * @returns {Roo.BoxComponent} this
23082      */
23083     setPagePosition : function(x, y){
23084         this.pageX = x;
23085         this.pageY = y;
23086         if(!this.boxReady){
23087             return;
23088         }
23089         if(x === undefined || y === undefined){ // cannot translate undefined points
23090             return;
23091         }
23092         var p = this.el.translatePoints(x, y);
23093         this.setPosition(p.left, p.top);
23094         return this;
23095     },
23096
23097     // private
23098     onRender : function(ct, position){
23099         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23100         if(this.resizeEl){
23101             this.resizeEl = Roo.get(this.resizeEl);
23102         }
23103         if(this.positionEl){
23104             this.positionEl = Roo.get(this.positionEl);
23105         }
23106     },
23107
23108     // private
23109     afterRender : function(){
23110         Roo.BoxComponent.superclass.afterRender.call(this);
23111         this.boxReady = true;
23112         this.setSize(this.width, this.height);
23113         if(this.x || this.y){
23114             this.setPosition(this.x, this.y);
23115         }
23116         if(this.pageX || this.pageY){
23117             this.setPagePosition(this.pageX, this.pageY);
23118         }
23119     },
23120
23121     /**
23122      * Force the component's size to recalculate based on the underlying element's current height and width.
23123      * @returns {Roo.BoxComponent} this
23124      */
23125     syncSize : function(){
23126         delete this.lastSize;
23127         this.setSize(this.el.getWidth(), this.el.getHeight());
23128         return this;
23129     },
23130
23131     /**
23132      * Called after the component is resized, this method is empty by default but can be implemented by any
23133      * subclass that needs to perform custom logic after a resize occurs.
23134      * @param {Number} adjWidth The box-adjusted width that was set
23135      * @param {Number} adjHeight The box-adjusted height that was set
23136      * @param {Number} rawWidth The width that was originally specified
23137      * @param {Number} rawHeight The height that was originally specified
23138      */
23139     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23140
23141     },
23142
23143     /**
23144      * Called after the component is moved, this method is empty by default but can be implemented by any
23145      * subclass that needs to perform custom logic after a move occurs.
23146      * @param {Number} x The new x position
23147      * @param {Number} y The new y position
23148      */
23149     onPosition : function(x, y){
23150
23151     },
23152
23153     // private
23154     adjustSize : function(w, h){
23155         if(this.autoWidth){
23156             w = 'auto';
23157         }
23158         if(this.autoHeight){
23159             h = 'auto';
23160         }
23161         return {width : w, height: h};
23162     },
23163
23164     // private
23165     adjustPosition : function(x, y){
23166         return {x : x, y: y};
23167     }
23168 });/*
23169  * Based on:
23170  * Ext JS Library 1.1.1
23171  * Copyright(c) 2006-2007, Ext JS, LLC.
23172  *
23173  * Originally Released Under LGPL - original licence link has changed is not relivant.
23174  *
23175  * Fork - LGPL
23176  * <script type="text/javascript">
23177  */
23178
23179
23180 /**
23181  * @class Roo.SplitBar
23182  * @extends Roo.util.Observable
23183  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23184  * <br><br>
23185  * Usage:
23186  * <pre><code>
23187 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23188                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23189 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23190 split.minSize = 100;
23191 split.maxSize = 600;
23192 split.animate = true;
23193 split.on('moved', splitterMoved);
23194 </code></pre>
23195  * @constructor
23196  * Create a new SplitBar
23197  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23198  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23199  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23200  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23201                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23202                         position of the SplitBar).
23203  */
23204 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23205     
23206     /** @private */
23207     this.el = Roo.get(dragElement, true);
23208     this.el.dom.unselectable = "on";
23209     /** @private */
23210     this.resizingEl = Roo.get(resizingElement, true);
23211
23212     /**
23213      * @private
23214      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23215      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23216      * @type Number
23217      */
23218     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23219     
23220     /**
23221      * The minimum size of the resizing element. (Defaults to 0)
23222      * @type Number
23223      */
23224     this.minSize = 0;
23225     
23226     /**
23227      * The maximum size of the resizing element. (Defaults to 2000)
23228      * @type Number
23229      */
23230     this.maxSize = 2000;
23231     
23232     /**
23233      * Whether to animate the transition to the new size
23234      * @type Boolean
23235      */
23236     this.animate = false;
23237     
23238     /**
23239      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23240      * @type Boolean
23241      */
23242     this.useShim = false;
23243     
23244     /** @private */
23245     this.shim = null;
23246     
23247     if(!existingProxy){
23248         /** @private */
23249         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23250     }else{
23251         this.proxy = Roo.get(existingProxy).dom;
23252     }
23253     /** @private */
23254     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23255     
23256     /** @private */
23257     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23258     
23259     /** @private */
23260     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23261     
23262     /** @private */
23263     this.dragSpecs = {};
23264     
23265     /**
23266      * @private The adapter to use to positon and resize elements
23267      */
23268     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23269     this.adapter.init(this);
23270     
23271     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23272         /** @private */
23273         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23274         this.el.addClass("x-splitbar-h");
23275     }else{
23276         /** @private */
23277         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23278         this.el.addClass("x-splitbar-v");
23279     }
23280     
23281     this.addEvents({
23282         /**
23283          * @event resize
23284          * Fires when the splitter is moved (alias for {@link #event-moved})
23285          * @param {Roo.SplitBar} this
23286          * @param {Number} newSize the new width or height
23287          */
23288         "resize" : true,
23289         /**
23290          * @event moved
23291          * Fires when the splitter is moved
23292          * @param {Roo.SplitBar} this
23293          * @param {Number} newSize the new width or height
23294          */
23295         "moved" : true,
23296         /**
23297          * @event beforeresize
23298          * Fires before the splitter is dragged
23299          * @param {Roo.SplitBar} this
23300          */
23301         "beforeresize" : true,
23302
23303         "beforeapply" : true
23304     });
23305
23306     Roo.util.Observable.call(this);
23307 };
23308
23309 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23310     onStartProxyDrag : function(x, y){
23311         this.fireEvent("beforeresize", this);
23312         if(!this.overlay){
23313             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23314             o.unselectable();
23315             o.enableDisplayMode("block");
23316             // all splitbars share the same overlay
23317             Roo.SplitBar.prototype.overlay = o;
23318         }
23319         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23320         this.overlay.show();
23321         Roo.get(this.proxy).setDisplayed("block");
23322         var size = this.adapter.getElementSize(this);
23323         this.activeMinSize = this.getMinimumSize();;
23324         this.activeMaxSize = this.getMaximumSize();;
23325         var c1 = size - this.activeMinSize;
23326         var c2 = Math.max(this.activeMaxSize - size, 0);
23327         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23328             this.dd.resetConstraints();
23329             this.dd.setXConstraint(
23330                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23331                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23332             );
23333             this.dd.setYConstraint(0, 0);
23334         }else{
23335             this.dd.resetConstraints();
23336             this.dd.setXConstraint(0, 0);
23337             this.dd.setYConstraint(
23338                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23339                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23340             );
23341          }
23342         this.dragSpecs.startSize = size;
23343         this.dragSpecs.startPoint = [x, y];
23344         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23345     },
23346     
23347     /** 
23348      * @private Called after the drag operation by the DDProxy
23349      */
23350     onEndProxyDrag : function(e){
23351         Roo.get(this.proxy).setDisplayed(false);
23352         var endPoint = Roo.lib.Event.getXY(e);
23353         if(this.overlay){
23354             this.overlay.hide();
23355         }
23356         var newSize;
23357         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23358             newSize = this.dragSpecs.startSize + 
23359                 (this.placement == Roo.SplitBar.LEFT ?
23360                     endPoint[0] - this.dragSpecs.startPoint[0] :
23361                     this.dragSpecs.startPoint[0] - endPoint[0]
23362                 );
23363         }else{
23364             newSize = this.dragSpecs.startSize + 
23365                 (this.placement == Roo.SplitBar.TOP ?
23366                     endPoint[1] - this.dragSpecs.startPoint[1] :
23367                     this.dragSpecs.startPoint[1] - endPoint[1]
23368                 );
23369         }
23370         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23371         if(newSize != this.dragSpecs.startSize){
23372             if(this.fireEvent('beforeapply', this, newSize) !== false){
23373                 this.adapter.setElementSize(this, newSize);
23374                 this.fireEvent("moved", this, newSize);
23375                 this.fireEvent("resize", this, newSize);
23376             }
23377         }
23378     },
23379     
23380     /**
23381      * Get the adapter this SplitBar uses
23382      * @return The adapter object
23383      */
23384     getAdapter : function(){
23385         return this.adapter;
23386     },
23387     
23388     /**
23389      * Set the adapter this SplitBar uses
23390      * @param {Object} adapter A SplitBar adapter object
23391      */
23392     setAdapter : function(adapter){
23393         this.adapter = adapter;
23394         this.adapter.init(this);
23395     },
23396     
23397     /**
23398      * Gets the minimum size for the resizing element
23399      * @return {Number} The minimum size
23400      */
23401     getMinimumSize : function(){
23402         return this.minSize;
23403     },
23404     
23405     /**
23406      * Sets the minimum size for the resizing element
23407      * @param {Number} minSize The minimum size
23408      */
23409     setMinimumSize : function(minSize){
23410         this.minSize = minSize;
23411     },
23412     
23413     /**
23414      * Gets the maximum size for the resizing element
23415      * @return {Number} The maximum size
23416      */
23417     getMaximumSize : function(){
23418         return this.maxSize;
23419     },
23420     
23421     /**
23422      * Sets the maximum size for the resizing element
23423      * @param {Number} maxSize The maximum size
23424      */
23425     setMaximumSize : function(maxSize){
23426         this.maxSize = maxSize;
23427     },
23428     
23429     /**
23430      * Sets the initialize size for the resizing element
23431      * @param {Number} size The initial size
23432      */
23433     setCurrentSize : function(size){
23434         var oldAnimate = this.animate;
23435         this.animate = false;
23436         this.adapter.setElementSize(this, size);
23437         this.animate = oldAnimate;
23438     },
23439     
23440     /**
23441      * Destroy this splitbar. 
23442      * @param {Boolean} removeEl True to remove the element
23443      */
23444     destroy : function(removeEl){
23445         if(this.shim){
23446             this.shim.remove();
23447         }
23448         this.dd.unreg();
23449         this.proxy.parentNode.removeChild(this.proxy);
23450         if(removeEl){
23451             this.el.remove();
23452         }
23453     }
23454 });
23455
23456 /**
23457  * @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.
23458  */
23459 Roo.SplitBar.createProxy = function(dir){
23460     var proxy = new Roo.Element(document.createElement("div"));
23461     proxy.unselectable();
23462     var cls = 'x-splitbar-proxy';
23463     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23464     document.body.appendChild(proxy.dom);
23465     return proxy.dom;
23466 };
23467
23468 /** 
23469  * @class Roo.SplitBar.BasicLayoutAdapter
23470  * Default Adapter. It assumes the splitter and resizing element are not positioned
23471  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23472  */
23473 Roo.SplitBar.BasicLayoutAdapter = function(){
23474 };
23475
23476 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23477     // do nothing for now
23478     init : function(s){
23479     
23480     },
23481     /**
23482      * Called before drag operations to get the current size of the resizing element. 
23483      * @param {Roo.SplitBar} s The SplitBar using this adapter
23484      */
23485      getElementSize : function(s){
23486         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23487             return s.resizingEl.getWidth();
23488         }else{
23489             return s.resizingEl.getHeight();
23490         }
23491     },
23492     
23493     /**
23494      * Called after drag operations to set the size of the resizing element.
23495      * @param {Roo.SplitBar} s The SplitBar using this adapter
23496      * @param {Number} newSize The new size to set
23497      * @param {Function} onComplete A function to be invoked when resizing is complete
23498      */
23499     setElementSize : function(s, newSize, onComplete){
23500         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23501             if(!s.animate){
23502                 s.resizingEl.setWidth(newSize);
23503                 if(onComplete){
23504                     onComplete(s, newSize);
23505                 }
23506             }else{
23507                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23508             }
23509         }else{
23510             
23511             if(!s.animate){
23512                 s.resizingEl.setHeight(newSize);
23513                 if(onComplete){
23514                     onComplete(s, newSize);
23515                 }
23516             }else{
23517                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23518             }
23519         }
23520     }
23521 };
23522
23523 /** 
23524  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23525  * @extends Roo.SplitBar.BasicLayoutAdapter
23526  * Adapter that  moves the splitter element to align with the resized sizing element. 
23527  * Used with an absolute positioned SplitBar.
23528  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23529  * document.body, make sure you assign an id to the body element.
23530  */
23531 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23532     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23533     this.container = Roo.get(container);
23534 };
23535
23536 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23537     init : function(s){
23538         this.basic.init(s);
23539     },
23540     
23541     getElementSize : function(s){
23542         return this.basic.getElementSize(s);
23543     },
23544     
23545     setElementSize : function(s, newSize, onComplete){
23546         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23547     },
23548     
23549     moveSplitter : function(s){
23550         var yes = Roo.SplitBar;
23551         switch(s.placement){
23552             case yes.LEFT:
23553                 s.el.setX(s.resizingEl.getRight());
23554                 break;
23555             case yes.RIGHT:
23556                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23557                 break;
23558             case yes.TOP:
23559                 s.el.setY(s.resizingEl.getBottom());
23560                 break;
23561             case yes.BOTTOM:
23562                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23563                 break;
23564         }
23565     }
23566 };
23567
23568 /**
23569  * Orientation constant - Create a vertical SplitBar
23570  * @static
23571  * @type Number
23572  */
23573 Roo.SplitBar.VERTICAL = 1;
23574
23575 /**
23576  * Orientation constant - Create a horizontal SplitBar
23577  * @static
23578  * @type Number
23579  */
23580 Roo.SplitBar.HORIZONTAL = 2;
23581
23582 /**
23583  * Placement constant - The resizing element is to the left of the splitter element
23584  * @static
23585  * @type Number
23586  */
23587 Roo.SplitBar.LEFT = 1;
23588
23589 /**
23590  * Placement constant - The resizing element is to the right of the splitter element
23591  * @static
23592  * @type Number
23593  */
23594 Roo.SplitBar.RIGHT = 2;
23595
23596 /**
23597  * Placement constant - The resizing element is positioned above the splitter element
23598  * @static
23599  * @type Number
23600  */
23601 Roo.SplitBar.TOP = 3;
23602
23603 /**
23604  * Placement constant - The resizing element is positioned under splitter element
23605  * @static
23606  * @type Number
23607  */
23608 Roo.SplitBar.BOTTOM = 4;
23609 /*
23610  * Based on:
23611  * Ext JS Library 1.1.1
23612  * Copyright(c) 2006-2007, Ext JS, LLC.
23613  *
23614  * Originally Released Under LGPL - original licence link has changed is not relivant.
23615  *
23616  * Fork - LGPL
23617  * <script type="text/javascript">
23618  */
23619
23620 /**
23621  * @class Roo.View
23622  * @extends Roo.util.Observable
23623  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23624  * This class also supports single and multi selection modes. <br>
23625  * Create a data model bound view:
23626  <pre><code>
23627  var store = new Roo.data.Store(...);
23628
23629  var view = new Roo.View({
23630     el : "my-element",
23631     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23632  
23633     singleSelect: true,
23634     selectedClass: "ydataview-selected",
23635     store: store
23636  });
23637
23638  // listen for node click?
23639  view.on("click", function(vw, index, node, e){
23640  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23641  });
23642
23643  // load XML data
23644  dataModel.load("foobar.xml");
23645  </code></pre>
23646  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23647  * <br><br>
23648  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23649  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23650  * 
23651  * Note: old style constructor is still suported (container, template, config)
23652  * 
23653  * @constructor
23654  * Create a new View
23655  * @param {Object} config The config object
23656  * 
23657  */
23658 Roo.View = function(config, depreciated_tpl, depreciated_config){
23659     
23660     if (typeof(depreciated_tpl) == 'undefined') {
23661         // new way.. - universal constructor.
23662         Roo.apply(this, config);
23663         this.el  = Roo.get(this.el);
23664     } else {
23665         // old format..
23666         this.el  = Roo.get(config);
23667         this.tpl = depreciated_tpl;
23668         Roo.apply(this, depreciated_config);
23669     }
23670      
23671     
23672     if(typeof(this.tpl) == "string"){
23673         this.tpl = new Roo.Template(this.tpl);
23674     } else {
23675         // support xtype ctors..
23676         this.tpl = new Roo.factory(this.tpl, Roo);
23677     }
23678     
23679     
23680     this.tpl.compile();
23681    
23682
23683      
23684     /** @private */
23685     this.addEvents({
23686         /**
23687          * @event beforeclick
23688          * Fires before a click is processed. Returns false to cancel the default action.
23689          * @param {Roo.View} this
23690          * @param {Number} index The index of the target node
23691          * @param {HTMLElement} node The target node
23692          * @param {Roo.EventObject} e The raw event object
23693          */
23694             "beforeclick" : true,
23695         /**
23696          * @event click
23697          * Fires when a template node is clicked.
23698          * @param {Roo.View} this
23699          * @param {Number} index The index of the target node
23700          * @param {HTMLElement} node The target node
23701          * @param {Roo.EventObject} e The raw event object
23702          */
23703             "click" : true,
23704         /**
23705          * @event dblclick
23706          * Fires when a template node is double clicked.
23707          * @param {Roo.View} this
23708          * @param {Number} index The index of the target node
23709          * @param {HTMLElement} node The target node
23710          * @param {Roo.EventObject} e The raw event object
23711          */
23712             "dblclick" : true,
23713         /**
23714          * @event contextmenu
23715          * Fires when a template node is right clicked.
23716          * @param {Roo.View} this
23717          * @param {Number} index The index of the target node
23718          * @param {HTMLElement} node The target node
23719          * @param {Roo.EventObject} e The raw event object
23720          */
23721             "contextmenu" : true,
23722         /**
23723          * @event selectionchange
23724          * Fires when the selected nodes change.
23725          * @param {Roo.View} this
23726          * @param {Array} selections Array of the selected nodes
23727          */
23728             "selectionchange" : true,
23729     
23730         /**
23731          * @event beforeselect
23732          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23733          * @param {Roo.View} this
23734          * @param {HTMLElement} node The node to be selected
23735          * @param {Array} selections Array of currently selected nodes
23736          */
23737             "beforeselect" : true,
23738         /**
23739          * @event preparedata
23740          * Fires on every row to render, to allow you to change the data.
23741          * @param {Roo.View} this
23742          * @param {Object} data to be rendered (change this)
23743          */
23744           "preparedata" : true
23745         });
23746
23747     this.el.on({
23748         "click": this.onClick,
23749         "dblclick": this.onDblClick,
23750         "contextmenu": this.onContextMenu,
23751         scope:this
23752     });
23753
23754     this.selections = [];
23755     this.nodes = [];
23756     this.cmp = new Roo.CompositeElementLite([]);
23757     if(this.store){
23758         this.store = Roo.factory(this.store, Roo.data);
23759         this.setStore(this.store, true);
23760     }
23761     Roo.View.superclass.constructor.call(this);
23762 };
23763
23764 Roo.extend(Roo.View, Roo.util.Observable, {
23765     
23766      /**
23767      * @cfg {Roo.data.Store} store Data store to load data from.
23768      */
23769     store : false,
23770     
23771     /**
23772      * @cfg {String|Roo.Element} el The container element.
23773      */
23774     el : '',
23775     
23776     /**
23777      * @cfg {String|Roo.Template} tpl The template used by this View 
23778      */
23779     tpl : false,
23780     /**
23781      * @cfg {String} dataName the named area of the template to use as the data area
23782      *                          Works with domtemplates roo-name="name"
23783      */
23784     dataName: false,
23785     /**
23786      * @cfg {String} selectedClass The css class to add to selected nodes
23787      */
23788     selectedClass : "x-view-selected",
23789      /**
23790      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23791      */
23792     emptyText : "",
23793     /**
23794      * @cfg {Boolean} multiSelect Allow multiple selection
23795      */
23796     multiSelect : false,
23797     /**
23798      * @cfg {Boolean} singleSelect Allow single selection
23799      */
23800     singleSelect:  false,
23801     
23802     /**
23803      * @cfg {Boolean} toggleSelect - selecting 
23804      */
23805     toggleSelect : false,
23806     
23807     /**
23808      * Returns the element this view is bound to.
23809      * @return {Roo.Element}
23810      */
23811     getEl : function(){
23812         return this.el;
23813     },
23814
23815     /**
23816      * Refreshes the view.
23817      */
23818     refresh : function(){
23819         var t = this.tpl;
23820         
23821         // if we are using something like 'domtemplate', then
23822         // the what gets used is:
23823         // t.applySubtemplate(NAME, data, wrapping data..)
23824         // the outer template then get' applied with
23825         //     the store 'extra data'
23826         // and the body get's added to the
23827         //      roo-name="data" node?
23828         //      <span class='roo-tpl-{name}'></span> ?????
23829         
23830         
23831         
23832         this.clearSelections();
23833         this.el.update("");
23834         var html = [];
23835         var records = this.store.getRange();
23836         if(records.length < 1) {
23837             
23838             // is this valid??  = should it render a template??
23839             
23840             this.el.update(this.emptyText);
23841             return;
23842         }
23843         var el = this.el;
23844         if (this.dataName) {
23845             this.el.update(t.apply(this.store.meta)); //????
23846             el = this.el.child('.roo-tpl-' + this.dataName);
23847         }
23848         
23849         for(var i = 0, len = records.length; i < len; i++){
23850             var data = this.prepareData(records[i].data, i, records[i]);
23851             this.fireEvent("preparedata", this, data, i, records[i]);
23852             html[html.length] = Roo.util.Format.trim(
23853                 this.dataName ?
23854                     t.applySubtemplate(this.dataName, data, this.store.meta) :
23855                     t.apply(data)
23856             );
23857         }
23858         
23859         
23860         
23861         el.update(html.join(""));
23862         this.nodes = el.dom.childNodes;
23863         this.updateIndexes(0);
23864     },
23865
23866     /**
23867      * Function to override to reformat the data that is sent to
23868      * the template for each node.
23869      * DEPRICATED - use the preparedata event handler.
23870      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23871      * a JSON object for an UpdateManager bound view).
23872      */
23873     prepareData : function(data, index, record)
23874     {
23875         this.fireEvent("preparedata", this, data, index, record);
23876         return data;
23877     },
23878
23879     onUpdate : function(ds, record){
23880         this.clearSelections();
23881         var index = this.store.indexOf(record);
23882         var n = this.nodes[index];
23883         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
23884         n.parentNode.removeChild(n);
23885         this.updateIndexes(index, index);
23886     },
23887
23888     
23889     
23890 // --------- FIXME     
23891     onAdd : function(ds, records, index)
23892     {
23893         this.clearSelections();
23894         if(this.nodes.length == 0){
23895             this.refresh();
23896             return;
23897         }
23898         var n = this.nodes[index];
23899         for(var i = 0, len = records.length; i < len; i++){
23900             var d = this.prepareData(records[i].data, i, records[i]);
23901             if(n){
23902                 this.tpl.insertBefore(n, d);
23903             }else{
23904                 
23905                 this.tpl.append(this.el, d);
23906             }
23907         }
23908         this.updateIndexes(index);
23909     },
23910
23911     onRemove : function(ds, record, index){
23912         this.clearSelections();
23913         var el = this.dataName  ?
23914             this.el.child('.roo-tpl-' + this.dataName) :
23915             this.el; 
23916         el.dom.removeChild(this.nodes[index]);
23917         this.updateIndexes(index);
23918     },
23919
23920     /**
23921      * Refresh an individual node.
23922      * @param {Number} index
23923      */
23924     refreshNode : function(index){
23925         this.onUpdate(this.store, this.store.getAt(index));
23926     },
23927
23928     updateIndexes : function(startIndex, endIndex){
23929         var ns = this.nodes;
23930         startIndex = startIndex || 0;
23931         endIndex = endIndex || ns.length - 1;
23932         for(var i = startIndex; i <= endIndex; i++){
23933             ns[i].nodeIndex = i;
23934         }
23935     },
23936
23937     /**
23938      * Changes the data store this view uses and refresh the view.
23939      * @param {Store} store
23940      */
23941     setStore : function(store, initial){
23942         if(!initial && this.store){
23943             this.store.un("datachanged", this.refresh);
23944             this.store.un("add", this.onAdd);
23945             this.store.un("remove", this.onRemove);
23946             this.store.un("update", this.onUpdate);
23947             this.store.un("clear", this.refresh);
23948         }
23949         if(store){
23950           
23951             store.on("datachanged", this.refresh, this);
23952             store.on("add", this.onAdd, this);
23953             store.on("remove", this.onRemove, this);
23954             store.on("update", this.onUpdate, this);
23955             store.on("clear", this.refresh, this);
23956         }
23957         
23958         if(store){
23959             this.refresh();
23960         }
23961     },
23962
23963     /**
23964      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23965      * @param {HTMLElement} node
23966      * @return {HTMLElement} The template node
23967      */
23968     findItemFromChild : function(node){
23969         var el = this.dataName  ?
23970             this.el.child('.roo-tpl-' + this.dataName,true) :
23971             this.el.dom; 
23972         
23973         if(!node || node.parentNode == el){
23974                     return node;
23975             }
23976             var p = node.parentNode;
23977             while(p && p != el){
23978             if(p.parentNode == el){
23979                 return p;
23980             }
23981             p = p.parentNode;
23982         }
23983             return null;
23984     },
23985
23986     /** @ignore */
23987     onClick : function(e){
23988         var item = this.findItemFromChild(e.getTarget());
23989         if(item){
23990             var index = this.indexOf(item);
23991             if(this.onItemClick(item, index, e) !== false){
23992                 this.fireEvent("click", this, index, item, e);
23993             }
23994         }else{
23995             this.clearSelections();
23996         }
23997     },
23998
23999     /** @ignore */
24000     onContextMenu : function(e){
24001         var item = this.findItemFromChild(e.getTarget());
24002         if(item){
24003             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24004         }
24005     },
24006
24007     /** @ignore */
24008     onDblClick : function(e){
24009         var item = this.findItemFromChild(e.getTarget());
24010         if(item){
24011             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24012         }
24013     },
24014
24015     onItemClick : function(item, index, e)
24016     {
24017         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24018             return false;
24019         }
24020         if (this.toggleSelect) {
24021             var m = this.isSelected(item) ? 'unselect' : 'select';
24022             Roo.log(m);
24023             var _t = this;
24024             _t[m](item, true, false);
24025             return true;
24026         }
24027         if(this.multiSelect || this.singleSelect){
24028             if(this.multiSelect && e.shiftKey && this.lastSelection){
24029                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24030             }else{
24031                 this.select(item, this.multiSelect && e.ctrlKey);
24032                 this.lastSelection = item;
24033             }
24034             e.preventDefault();
24035         }
24036         return true;
24037     },
24038
24039     /**
24040      * Get the number of selected nodes.
24041      * @return {Number}
24042      */
24043     getSelectionCount : function(){
24044         return this.selections.length;
24045     },
24046
24047     /**
24048      * Get the currently selected nodes.
24049      * @return {Array} An array of HTMLElements
24050      */
24051     getSelectedNodes : function(){
24052         return this.selections;
24053     },
24054
24055     /**
24056      * Get the indexes of the selected nodes.
24057      * @return {Array}
24058      */
24059     getSelectedIndexes : function(){
24060         var indexes = [], s = this.selections;
24061         for(var i = 0, len = s.length; i < len; i++){
24062             indexes.push(s[i].nodeIndex);
24063         }
24064         return indexes;
24065     },
24066
24067     /**
24068      * Clear all selections
24069      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24070      */
24071     clearSelections : function(suppressEvent){
24072         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24073             this.cmp.elements = this.selections;
24074             this.cmp.removeClass(this.selectedClass);
24075             this.selections = [];
24076             if(!suppressEvent){
24077                 this.fireEvent("selectionchange", this, this.selections);
24078             }
24079         }
24080     },
24081
24082     /**
24083      * Returns true if the passed node is selected
24084      * @param {HTMLElement/Number} node The node or node index
24085      * @return {Boolean}
24086      */
24087     isSelected : function(node){
24088         var s = this.selections;
24089         if(s.length < 1){
24090             return false;
24091         }
24092         node = this.getNode(node);
24093         return s.indexOf(node) !== -1;
24094     },
24095
24096     /**
24097      * Selects nodes.
24098      * @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
24099      * @param {Boolean} keepExisting (optional) true to keep existing selections
24100      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24101      */
24102     select : function(nodeInfo, keepExisting, suppressEvent){
24103         if(nodeInfo instanceof Array){
24104             if(!keepExisting){
24105                 this.clearSelections(true);
24106             }
24107             for(var i = 0, len = nodeInfo.length; i < len; i++){
24108                 this.select(nodeInfo[i], true, true);
24109             }
24110             return;
24111         } 
24112         var node = this.getNode(nodeInfo);
24113         if(!node || this.isSelected(node)){
24114             return; // already selected.
24115         }
24116         if(!keepExisting){
24117             this.clearSelections(true);
24118         }
24119         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24120             Roo.fly(node).addClass(this.selectedClass);
24121             this.selections.push(node);
24122             if(!suppressEvent){
24123                 this.fireEvent("selectionchange", this, this.selections);
24124             }
24125         }
24126         
24127         
24128     },
24129       /**
24130      * Unselects nodes.
24131      * @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
24132      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24133      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24134      */
24135     unselect : function(nodeInfo, keepExisting, suppressEvent)
24136     {
24137         if(nodeInfo instanceof Array){
24138             Roo.each(this.selections, function(s) {
24139                 this.unselect(s, nodeInfo);
24140             }, this);
24141             return;
24142         }
24143         var node = this.getNode(nodeInfo);
24144         if(!node || !this.isSelected(node)){
24145             Roo.log("not selected");
24146             return; // not selected.
24147         }
24148         // fireevent???
24149         var ns = [];
24150         Roo.each(this.selections, function(s) {
24151             if (s == node ) {
24152                 Roo.fly(node).removeClass(this.selectedClass);
24153
24154                 return;
24155             }
24156             ns.push(s);
24157         },this);
24158         
24159         this.selections= ns;
24160         this.fireEvent("selectionchange", this, this.selections);
24161     },
24162
24163     /**
24164      * Gets a template node.
24165      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24166      * @return {HTMLElement} The node or null if it wasn't found
24167      */
24168     getNode : function(nodeInfo){
24169         if(typeof nodeInfo == "string"){
24170             return document.getElementById(nodeInfo);
24171         }else if(typeof nodeInfo == "number"){
24172             return this.nodes[nodeInfo];
24173         }
24174         return nodeInfo;
24175     },
24176
24177     /**
24178      * Gets a range template nodes.
24179      * @param {Number} startIndex
24180      * @param {Number} endIndex
24181      * @return {Array} An array of nodes
24182      */
24183     getNodes : function(start, end){
24184         var ns = this.nodes;
24185         start = start || 0;
24186         end = typeof end == "undefined" ? ns.length - 1 : end;
24187         var nodes = [];
24188         if(start <= end){
24189             for(var i = start; i <= end; i++){
24190                 nodes.push(ns[i]);
24191             }
24192         } else{
24193             for(var i = start; i >= end; i--){
24194                 nodes.push(ns[i]);
24195             }
24196         }
24197         return nodes;
24198     },
24199
24200     /**
24201      * Finds the index of the passed node
24202      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24203      * @return {Number} The index of the node or -1
24204      */
24205     indexOf : function(node){
24206         node = this.getNode(node);
24207         if(typeof node.nodeIndex == "number"){
24208             return node.nodeIndex;
24209         }
24210         var ns = this.nodes;
24211         for(var i = 0, len = ns.length; i < len; i++){
24212             if(ns[i] == node){
24213                 return i;
24214             }
24215         }
24216         return -1;
24217     }
24218 });
24219 /*
24220  * Based on:
24221  * Ext JS Library 1.1.1
24222  * Copyright(c) 2006-2007, Ext JS, LLC.
24223  *
24224  * Originally Released Under LGPL - original licence link has changed is not relivant.
24225  *
24226  * Fork - LGPL
24227  * <script type="text/javascript">
24228  */
24229
24230 /**
24231  * @class Roo.JsonView
24232  * @extends Roo.View
24233  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24234 <pre><code>
24235 var view = new Roo.JsonView({
24236     container: "my-element",
24237     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24238     multiSelect: true, 
24239     jsonRoot: "data" 
24240 });
24241
24242 // listen for node click?
24243 view.on("click", function(vw, index, node, e){
24244     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24245 });
24246
24247 // direct load of JSON data
24248 view.load("foobar.php");
24249
24250 // Example from my blog list
24251 var tpl = new Roo.Template(
24252     '&lt;div class="entry"&gt;' +
24253     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24254     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24255     "&lt;/div&gt;&lt;hr /&gt;"
24256 );
24257
24258 var moreView = new Roo.JsonView({
24259     container :  "entry-list", 
24260     template : tpl,
24261     jsonRoot: "posts"
24262 });
24263 moreView.on("beforerender", this.sortEntries, this);
24264 moreView.load({
24265     url: "/blog/get-posts.php",
24266     params: "allposts=true",
24267     text: "Loading Blog Entries..."
24268 });
24269 </code></pre>
24270
24271 * Note: old code is supported with arguments : (container, template, config)
24272
24273
24274  * @constructor
24275  * Create a new JsonView
24276  * 
24277  * @param {Object} config The config object
24278  * 
24279  */
24280 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24281     
24282     
24283     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24284
24285     var um = this.el.getUpdateManager();
24286     um.setRenderer(this);
24287     um.on("update", this.onLoad, this);
24288     um.on("failure", this.onLoadException, this);
24289
24290     /**
24291      * @event beforerender
24292      * Fires before rendering of the downloaded JSON data.
24293      * @param {Roo.JsonView} this
24294      * @param {Object} data The JSON data loaded
24295      */
24296     /**
24297      * @event load
24298      * Fires when data is loaded.
24299      * @param {Roo.JsonView} this
24300      * @param {Object} data The JSON data loaded
24301      * @param {Object} response The raw Connect response object
24302      */
24303     /**
24304      * @event loadexception
24305      * Fires when loading fails.
24306      * @param {Roo.JsonView} this
24307      * @param {Object} response The raw Connect response object
24308      */
24309     this.addEvents({
24310         'beforerender' : true,
24311         'load' : true,
24312         'loadexception' : true
24313     });
24314 };
24315 Roo.extend(Roo.JsonView, Roo.View, {
24316     /**
24317      * @type {String} The root property in the loaded JSON object that contains the data
24318      */
24319     jsonRoot : "",
24320
24321     /**
24322      * Refreshes the view.
24323      */
24324     refresh : function(){
24325         this.clearSelections();
24326         this.el.update("");
24327         var html = [];
24328         var o = this.jsonData;
24329         if(o && o.length > 0){
24330             for(var i = 0, len = o.length; i < len; i++){
24331                 var data = this.prepareData(o[i], i, o);
24332                 html[html.length] = this.tpl.apply(data);
24333             }
24334         }else{
24335             html.push(this.emptyText);
24336         }
24337         this.el.update(html.join(""));
24338         this.nodes = this.el.dom.childNodes;
24339         this.updateIndexes(0);
24340     },
24341
24342     /**
24343      * 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.
24344      * @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:
24345      <pre><code>
24346      view.load({
24347          url: "your-url.php",
24348          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24349          callback: yourFunction,
24350          scope: yourObject, //(optional scope)
24351          discardUrl: false,
24352          nocache: false,
24353          text: "Loading...",
24354          timeout: 30,
24355          scripts: false
24356      });
24357      </code></pre>
24358      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24359      * 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.
24360      * @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}
24361      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24362      * @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.
24363      */
24364     load : function(){
24365         var um = this.el.getUpdateManager();
24366         um.update.apply(um, arguments);
24367     },
24368
24369     render : function(el, response){
24370         this.clearSelections();
24371         this.el.update("");
24372         var o;
24373         try{
24374             o = Roo.util.JSON.decode(response.responseText);
24375             if(this.jsonRoot){
24376                 
24377                 o = o[this.jsonRoot];
24378             }
24379         } catch(e){
24380         }
24381         /**
24382          * The current JSON data or null
24383          */
24384         this.jsonData = o;
24385         this.beforeRender();
24386         this.refresh();
24387     },
24388
24389 /**
24390  * Get the number of records in the current JSON dataset
24391  * @return {Number}
24392  */
24393     getCount : function(){
24394         return this.jsonData ? this.jsonData.length : 0;
24395     },
24396
24397 /**
24398  * Returns the JSON object for the specified node(s)
24399  * @param {HTMLElement/Array} node The node or an array of nodes
24400  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24401  * you get the JSON object for the node
24402  */
24403     getNodeData : function(node){
24404         if(node instanceof Array){
24405             var data = [];
24406             for(var i = 0, len = node.length; i < len; i++){
24407                 data.push(this.getNodeData(node[i]));
24408             }
24409             return data;
24410         }
24411         return this.jsonData[this.indexOf(node)] || null;
24412     },
24413
24414     beforeRender : function(){
24415         this.snapshot = this.jsonData;
24416         if(this.sortInfo){
24417             this.sort.apply(this, this.sortInfo);
24418         }
24419         this.fireEvent("beforerender", this, this.jsonData);
24420     },
24421
24422     onLoad : function(el, o){
24423         this.fireEvent("load", this, this.jsonData, o);
24424     },
24425
24426     onLoadException : function(el, o){
24427         this.fireEvent("loadexception", this, o);
24428     },
24429
24430 /**
24431  * Filter the data by a specific property.
24432  * @param {String} property A property on your JSON objects
24433  * @param {String/RegExp} value Either string that the property values
24434  * should start with, or a RegExp to test against the property
24435  */
24436     filter : function(property, value){
24437         if(this.jsonData){
24438             var data = [];
24439             var ss = this.snapshot;
24440             if(typeof value == "string"){
24441                 var vlen = value.length;
24442                 if(vlen == 0){
24443                     this.clearFilter();
24444                     return;
24445                 }
24446                 value = value.toLowerCase();
24447                 for(var i = 0, len = ss.length; i < len; i++){
24448                     var o = ss[i];
24449                     if(o[property].substr(0, vlen).toLowerCase() == value){
24450                         data.push(o);
24451                     }
24452                 }
24453             } else if(value.exec){ // regex?
24454                 for(var i = 0, len = ss.length; i < len; i++){
24455                     var o = ss[i];
24456                     if(value.test(o[property])){
24457                         data.push(o);
24458                     }
24459                 }
24460             } else{
24461                 return;
24462             }
24463             this.jsonData = data;
24464             this.refresh();
24465         }
24466     },
24467
24468 /**
24469  * Filter by a function. The passed function will be called with each
24470  * object in the current dataset. If the function returns true the value is kept,
24471  * otherwise it is filtered.
24472  * @param {Function} fn
24473  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24474  */
24475     filterBy : function(fn, scope){
24476         if(this.jsonData){
24477             var data = [];
24478             var ss = this.snapshot;
24479             for(var i = 0, len = ss.length; i < len; i++){
24480                 var o = ss[i];
24481                 if(fn.call(scope || this, o)){
24482                     data.push(o);
24483                 }
24484             }
24485             this.jsonData = data;
24486             this.refresh();
24487         }
24488     },
24489
24490 /**
24491  * Clears the current filter.
24492  */
24493     clearFilter : function(){
24494         if(this.snapshot && this.jsonData != this.snapshot){
24495             this.jsonData = this.snapshot;
24496             this.refresh();
24497         }
24498     },
24499
24500
24501 /**
24502  * Sorts the data for this view and refreshes it.
24503  * @param {String} property A property on your JSON objects to sort on
24504  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24505  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24506  */
24507     sort : function(property, dir, sortType){
24508         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24509         if(this.jsonData){
24510             var p = property;
24511             var dsc = dir && dir.toLowerCase() == "desc";
24512             var f = function(o1, o2){
24513                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24514                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24515                 ;
24516                 if(v1 < v2){
24517                     return dsc ? +1 : -1;
24518                 } else if(v1 > v2){
24519                     return dsc ? -1 : +1;
24520                 } else{
24521                     return 0;
24522                 }
24523             };
24524             this.jsonData.sort(f);
24525             this.refresh();
24526             if(this.jsonData != this.snapshot){
24527                 this.snapshot.sort(f);
24528             }
24529         }
24530     }
24531 });/*
24532  * Based on:
24533  * Ext JS Library 1.1.1
24534  * Copyright(c) 2006-2007, Ext JS, LLC.
24535  *
24536  * Originally Released Under LGPL - original licence link has changed is not relivant.
24537  *
24538  * Fork - LGPL
24539  * <script type="text/javascript">
24540  */
24541  
24542
24543 /**
24544  * @class Roo.ColorPalette
24545  * @extends Roo.Component
24546  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24547  * Here's an example of typical usage:
24548  * <pre><code>
24549 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24550 cp.render('my-div');
24551
24552 cp.on('select', function(palette, selColor){
24553     // do something with selColor
24554 });
24555 </code></pre>
24556  * @constructor
24557  * Create a new ColorPalette
24558  * @param {Object} config The config object
24559  */
24560 Roo.ColorPalette = function(config){
24561     Roo.ColorPalette.superclass.constructor.call(this, config);
24562     this.addEvents({
24563         /**
24564              * @event select
24565              * Fires when a color is selected
24566              * @param {ColorPalette} this
24567              * @param {String} color The 6-digit color hex code (without the # symbol)
24568              */
24569         select: true
24570     });
24571
24572     if(this.handler){
24573         this.on("select", this.handler, this.scope, true);
24574     }
24575 };
24576 Roo.extend(Roo.ColorPalette, Roo.Component, {
24577     /**
24578      * @cfg {String} itemCls
24579      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24580      */
24581     itemCls : "x-color-palette",
24582     /**
24583      * @cfg {String} value
24584      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24585      * the hex codes are case-sensitive.
24586      */
24587     value : null,
24588     clickEvent:'click',
24589     // private
24590     ctype: "Roo.ColorPalette",
24591
24592     /**
24593      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24594      */
24595     allowReselect : false,
24596
24597     /**
24598      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24599      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24600      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24601      * of colors with the width setting until the box is symmetrical.</p>
24602      * <p>You can override individual colors if needed:</p>
24603      * <pre><code>
24604 var cp = new Roo.ColorPalette();
24605 cp.colors[0] = "FF0000";  // change the first box to red
24606 </code></pre>
24607
24608 Or you can provide a custom array of your own for complete control:
24609 <pre><code>
24610 var cp = new Roo.ColorPalette();
24611 cp.colors = ["000000", "993300", "333300"];
24612 </code></pre>
24613      * @type Array
24614      */
24615     colors : [
24616         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24617         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24618         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24619         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24620         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24621     ],
24622
24623     // private
24624     onRender : function(container, position){
24625         var t = new Roo.MasterTemplate(
24626             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24627         );
24628         var c = this.colors;
24629         for(var i = 0, len = c.length; i < len; i++){
24630             t.add([c[i]]);
24631         }
24632         var el = document.createElement("div");
24633         el.className = this.itemCls;
24634         t.overwrite(el);
24635         container.dom.insertBefore(el, position);
24636         this.el = Roo.get(el);
24637         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24638         if(this.clickEvent != 'click'){
24639             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24640         }
24641     },
24642
24643     // private
24644     afterRender : function(){
24645         Roo.ColorPalette.superclass.afterRender.call(this);
24646         if(this.value){
24647             var s = this.value;
24648             this.value = null;
24649             this.select(s);
24650         }
24651     },
24652
24653     // private
24654     handleClick : function(e, t){
24655         e.preventDefault();
24656         if(!this.disabled){
24657             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24658             this.select(c.toUpperCase());
24659         }
24660     },
24661
24662     /**
24663      * Selects the specified color in the palette (fires the select event)
24664      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24665      */
24666     select : function(color){
24667         color = color.replace("#", "");
24668         if(color != this.value || this.allowReselect){
24669             var el = this.el;
24670             if(this.value){
24671                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24672             }
24673             el.child("a.color-"+color).addClass("x-color-palette-sel");
24674             this.value = color;
24675             this.fireEvent("select", this, color);
24676         }
24677     }
24678 });/*
24679  * Based on:
24680  * Ext JS Library 1.1.1
24681  * Copyright(c) 2006-2007, Ext JS, LLC.
24682  *
24683  * Originally Released Under LGPL - original licence link has changed is not relivant.
24684  *
24685  * Fork - LGPL
24686  * <script type="text/javascript">
24687  */
24688  
24689 /**
24690  * @class Roo.DatePicker
24691  * @extends Roo.Component
24692  * Simple date picker class.
24693  * @constructor
24694  * Create a new DatePicker
24695  * @param {Object} config The config object
24696  */
24697 Roo.DatePicker = function(config){
24698     Roo.DatePicker.superclass.constructor.call(this, config);
24699
24700     this.value = config && config.value ?
24701                  config.value.clearTime() : new Date().clearTime();
24702
24703     this.addEvents({
24704         /**
24705              * @event select
24706              * Fires when a date is selected
24707              * @param {DatePicker} this
24708              * @param {Date} date The selected date
24709              */
24710         'select': true,
24711         /**
24712              * @event monthchange
24713              * Fires when the displayed month changes 
24714              * @param {DatePicker} this
24715              * @param {Date} date The selected month
24716              */
24717         'monthchange': true
24718     });
24719
24720     if(this.handler){
24721         this.on("select", this.handler,  this.scope || this);
24722     }
24723     // build the disabledDatesRE
24724     if(!this.disabledDatesRE && this.disabledDates){
24725         var dd = this.disabledDates;
24726         var re = "(?:";
24727         for(var i = 0; i < dd.length; i++){
24728             re += dd[i];
24729             if(i != dd.length-1) re += "|";
24730         }
24731         this.disabledDatesRE = new RegExp(re + ")");
24732     }
24733 };
24734
24735 Roo.extend(Roo.DatePicker, Roo.Component, {
24736     /**
24737      * @cfg {String} todayText
24738      * The text to display on the button that selects the current date (defaults to "Today")
24739      */
24740     todayText : "Today",
24741     /**
24742      * @cfg {String} okText
24743      * The text to display on the ok button
24744      */
24745     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24746     /**
24747      * @cfg {String} cancelText
24748      * The text to display on the cancel button
24749      */
24750     cancelText : "Cancel",
24751     /**
24752      * @cfg {String} todayTip
24753      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24754      */
24755     todayTip : "{0} (Spacebar)",
24756     /**
24757      * @cfg {Date} minDate
24758      * Minimum allowable date (JavaScript date object, defaults to null)
24759      */
24760     minDate : null,
24761     /**
24762      * @cfg {Date} maxDate
24763      * Maximum allowable date (JavaScript date object, defaults to null)
24764      */
24765     maxDate : null,
24766     /**
24767      * @cfg {String} minText
24768      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24769      */
24770     minText : "This date is before the minimum date",
24771     /**
24772      * @cfg {String} maxText
24773      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24774      */
24775     maxText : "This date is after the maximum date",
24776     /**
24777      * @cfg {String} format
24778      * The default date format string which can be overriden for localization support.  The format must be
24779      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24780      */
24781     format : "m/d/y",
24782     /**
24783      * @cfg {Array} disabledDays
24784      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24785      */
24786     disabledDays : null,
24787     /**
24788      * @cfg {String} disabledDaysText
24789      * The tooltip to display when the date falls on a disabled day (defaults to "")
24790      */
24791     disabledDaysText : "",
24792     /**
24793      * @cfg {RegExp} disabledDatesRE
24794      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24795      */
24796     disabledDatesRE : null,
24797     /**
24798      * @cfg {String} disabledDatesText
24799      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24800      */
24801     disabledDatesText : "",
24802     /**
24803      * @cfg {Boolean} constrainToViewport
24804      * True to constrain the date picker to the viewport (defaults to true)
24805      */
24806     constrainToViewport : true,
24807     /**
24808      * @cfg {Array} monthNames
24809      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24810      */
24811     monthNames : Date.monthNames,
24812     /**
24813      * @cfg {Array} dayNames
24814      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24815      */
24816     dayNames : Date.dayNames,
24817     /**
24818      * @cfg {String} nextText
24819      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24820      */
24821     nextText: 'Next Month (Control+Right)',
24822     /**
24823      * @cfg {String} prevText
24824      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24825      */
24826     prevText: 'Previous Month (Control+Left)',
24827     /**
24828      * @cfg {String} monthYearText
24829      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24830      */
24831     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24832     /**
24833      * @cfg {Number} startDay
24834      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24835      */
24836     startDay : 0,
24837     /**
24838      * @cfg {Bool} showClear
24839      * Show a clear button (usefull for date form elements that can be blank.)
24840      */
24841     
24842     showClear: false,
24843     
24844     /**
24845      * Sets the value of the date field
24846      * @param {Date} value The date to set
24847      */
24848     setValue : function(value){
24849         var old = this.value;
24850         this.value = value.clearTime(true);
24851         if(this.el){
24852             this.update(this.value);
24853         }
24854     },
24855
24856     /**
24857      * Gets the current selected value of the date field
24858      * @return {Date} The selected date
24859      */
24860     getValue : function(){
24861         return this.value;
24862     },
24863
24864     // private
24865     focus : function(){
24866         if(this.el){
24867             this.update(this.activeDate);
24868         }
24869     },
24870
24871     // private
24872     onRender : function(container, position){
24873         var m = [
24874              '<table cellspacing="0">',
24875                 '<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>',
24876                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24877         var dn = this.dayNames;
24878         for(var i = 0; i < 7; i++){
24879             var d = this.startDay+i;
24880             if(d > 6){
24881                 d = d-7;
24882             }
24883             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24884         }
24885         m[m.length] = "</tr></thead><tbody><tr>";
24886         for(var i = 0; i < 42; i++) {
24887             if(i % 7 == 0 && i != 0){
24888                 m[m.length] = "</tr><tr>";
24889             }
24890             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24891         }
24892         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24893             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24894
24895         var el = document.createElement("div");
24896         el.className = "x-date-picker";
24897         el.innerHTML = m.join("");
24898
24899         container.dom.insertBefore(el, position);
24900
24901         this.el = Roo.get(el);
24902         this.eventEl = Roo.get(el.firstChild);
24903
24904         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24905             handler: this.showPrevMonth,
24906             scope: this,
24907             preventDefault:true,
24908             stopDefault:true
24909         });
24910
24911         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24912             handler: this.showNextMonth,
24913             scope: this,
24914             preventDefault:true,
24915             stopDefault:true
24916         });
24917
24918         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24919
24920         this.monthPicker = this.el.down('div.x-date-mp');
24921         this.monthPicker.enableDisplayMode('block');
24922         
24923         var kn = new Roo.KeyNav(this.eventEl, {
24924             "left" : function(e){
24925                 e.ctrlKey ?
24926                     this.showPrevMonth() :
24927                     this.update(this.activeDate.add("d", -1));
24928             },
24929
24930             "right" : function(e){
24931                 e.ctrlKey ?
24932                     this.showNextMonth() :
24933                     this.update(this.activeDate.add("d", 1));
24934             },
24935
24936             "up" : function(e){
24937                 e.ctrlKey ?
24938                     this.showNextYear() :
24939                     this.update(this.activeDate.add("d", -7));
24940             },
24941
24942             "down" : function(e){
24943                 e.ctrlKey ?
24944                     this.showPrevYear() :
24945                     this.update(this.activeDate.add("d", 7));
24946             },
24947
24948             "pageUp" : function(e){
24949                 this.showNextMonth();
24950             },
24951
24952             "pageDown" : function(e){
24953                 this.showPrevMonth();
24954             },
24955
24956             "enter" : function(e){
24957                 e.stopPropagation();
24958                 return true;
24959             },
24960
24961             scope : this
24962         });
24963
24964         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24965
24966         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24967
24968         this.el.unselectable();
24969         
24970         this.cells = this.el.select("table.x-date-inner tbody td");
24971         this.textNodes = this.el.query("table.x-date-inner tbody span");
24972
24973         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24974             text: "&#160;",
24975             tooltip: this.monthYearText
24976         });
24977
24978         this.mbtn.on('click', this.showMonthPicker, this);
24979         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24980
24981
24982         var today = (new Date()).dateFormat(this.format);
24983         
24984         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24985         if (this.showClear) {
24986             baseTb.add( new Roo.Toolbar.Fill());
24987         }
24988         baseTb.add({
24989             text: String.format(this.todayText, today),
24990             tooltip: String.format(this.todayTip, today),
24991             handler: this.selectToday,
24992             scope: this
24993         });
24994         
24995         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24996             
24997         //});
24998         if (this.showClear) {
24999             
25000             baseTb.add( new Roo.Toolbar.Fill());
25001             baseTb.add({
25002                 text: '&#160;',
25003                 cls: 'x-btn-icon x-btn-clear',
25004                 handler: function() {
25005                     //this.value = '';
25006                     this.fireEvent("select", this, '');
25007                 },
25008                 scope: this
25009             });
25010         }
25011         
25012         
25013         if(Roo.isIE){
25014             this.el.repaint();
25015         }
25016         this.update(this.value);
25017     },
25018
25019     createMonthPicker : function(){
25020         if(!this.monthPicker.dom.firstChild){
25021             var buf = ['<table border="0" cellspacing="0">'];
25022             for(var i = 0; i < 6; i++){
25023                 buf.push(
25024                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25025                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25026                     i == 0 ?
25027                     '<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>' :
25028                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25029                 );
25030             }
25031             buf.push(
25032                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25033                     this.okText,
25034                     '</button><button type="button" class="x-date-mp-cancel">',
25035                     this.cancelText,
25036                     '</button></td></tr>',
25037                 '</table>'
25038             );
25039             this.monthPicker.update(buf.join(''));
25040             this.monthPicker.on('click', this.onMonthClick, this);
25041             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25042
25043             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25044             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25045
25046             this.mpMonths.each(function(m, a, i){
25047                 i += 1;
25048                 if((i%2) == 0){
25049                     m.dom.xmonth = 5 + Math.round(i * .5);
25050                 }else{
25051                     m.dom.xmonth = Math.round((i-1) * .5);
25052                 }
25053             });
25054         }
25055     },
25056
25057     showMonthPicker : function(){
25058         this.createMonthPicker();
25059         var size = this.el.getSize();
25060         this.monthPicker.setSize(size);
25061         this.monthPicker.child('table').setSize(size);
25062
25063         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25064         this.updateMPMonth(this.mpSelMonth);
25065         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25066         this.updateMPYear(this.mpSelYear);
25067
25068         this.monthPicker.slideIn('t', {duration:.2});
25069     },
25070
25071     updateMPYear : function(y){
25072         this.mpyear = y;
25073         var ys = this.mpYears.elements;
25074         for(var i = 1; i <= 10; i++){
25075             var td = ys[i-1], y2;
25076             if((i%2) == 0){
25077                 y2 = y + Math.round(i * .5);
25078                 td.firstChild.innerHTML = y2;
25079                 td.xyear = y2;
25080             }else{
25081                 y2 = y - (5-Math.round(i * .5));
25082                 td.firstChild.innerHTML = y2;
25083                 td.xyear = y2;
25084             }
25085             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25086         }
25087     },
25088
25089     updateMPMonth : function(sm){
25090         this.mpMonths.each(function(m, a, i){
25091             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25092         });
25093     },
25094
25095     selectMPMonth: function(m){
25096         
25097     },
25098
25099     onMonthClick : function(e, t){
25100         e.stopEvent();
25101         var el = new Roo.Element(t), pn;
25102         if(el.is('button.x-date-mp-cancel')){
25103             this.hideMonthPicker();
25104         }
25105         else if(el.is('button.x-date-mp-ok')){
25106             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25107             this.hideMonthPicker();
25108         }
25109         else if(pn = el.up('td.x-date-mp-month', 2)){
25110             this.mpMonths.removeClass('x-date-mp-sel');
25111             pn.addClass('x-date-mp-sel');
25112             this.mpSelMonth = pn.dom.xmonth;
25113         }
25114         else if(pn = el.up('td.x-date-mp-year', 2)){
25115             this.mpYears.removeClass('x-date-mp-sel');
25116             pn.addClass('x-date-mp-sel');
25117             this.mpSelYear = pn.dom.xyear;
25118         }
25119         else if(el.is('a.x-date-mp-prev')){
25120             this.updateMPYear(this.mpyear-10);
25121         }
25122         else if(el.is('a.x-date-mp-next')){
25123             this.updateMPYear(this.mpyear+10);
25124         }
25125     },
25126
25127     onMonthDblClick : function(e, t){
25128         e.stopEvent();
25129         var el = new Roo.Element(t), pn;
25130         if(pn = el.up('td.x-date-mp-month', 2)){
25131             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25132             this.hideMonthPicker();
25133         }
25134         else if(pn = el.up('td.x-date-mp-year', 2)){
25135             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25136             this.hideMonthPicker();
25137         }
25138     },
25139
25140     hideMonthPicker : function(disableAnim){
25141         if(this.monthPicker){
25142             if(disableAnim === true){
25143                 this.monthPicker.hide();
25144             }else{
25145                 this.monthPicker.slideOut('t', {duration:.2});
25146             }
25147         }
25148     },
25149
25150     // private
25151     showPrevMonth : function(e){
25152         this.update(this.activeDate.add("mo", -1));
25153     },
25154
25155     // private
25156     showNextMonth : function(e){
25157         this.update(this.activeDate.add("mo", 1));
25158     },
25159
25160     // private
25161     showPrevYear : function(){
25162         this.update(this.activeDate.add("y", -1));
25163     },
25164
25165     // private
25166     showNextYear : function(){
25167         this.update(this.activeDate.add("y", 1));
25168     },
25169
25170     // private
25171     handleMouseWheel : function(e){
25172         var delta = e.getWheelDelta();
25173         if(delta > 0){
25174             this.showPrevMonth();
25175             e.stopEvent();
25176         } else if(delta < 0){
25177             this.showNextMonth();
25178             e.stopEvent();
25179         }
25180     },
25181
25182     // private
25183     handleDateClick : function(e, t){
25184         e.stopEvent();
25185         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25186             this.setValue(new Date(t.dateValue));
25187             this.fireEvent("select", this, this.value);
25188         }
25189     },
25190
25191     // private
25192     selectToday : function(){
25193         this.setValue(new Date().clearTime());
25194         this.fireEvent("select", this, this.value);
25195     },
25196
25197     // private
25198     update : function(date)
25199     {
25200         var vd = this.activeDate;
25201         this.activeDate = date;
25202         if(vd && this.el){
25203             var t = date.getTime();
25204             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25205                 this.cells.removeClass("x-date-selected");
25206                 this.cells.each(function(c){
25207                    if(c.dom.firstChild.dateValue == t){
25208                        c.addClass("x-date-selected");
25209                        setTimeout(function(){
25210                             try{c.dom.firstChild.focus();}catch(e){}
25211                        }, 50);
25212                        return false;
25213                    }
25214                 });
25215                 return;
25216             }
25217         }
25218         
25219         var days = date.getDaysInMonth();
25220         var firstOfMonth = date.getFirstDateOfMonth();
25221         var startingPos = firstOfMonth.getDay()-this.startDay;
25222
25223         if(startingPos <= this.startDay){
25224             startingPos += 7;
25225         }
25226
25227         var pm = date.add("mo", -1);
25228         var prevStart = pm.getDaysInMonth()-startingPos;
25229
25230         var cells = this.cells.elements;
25231         var textEls = this.textNodes;
25232         days += startingPos;
25233
25234         // convert everything to numbers so it's fast
25235         var day = 86400000;
25236         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25237         var today = new Date().clearTime().getTime();
25238         var sel = date.clearTime().getTime();
25239         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25240         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25241         var ddMatch = this.disabledDatesRE;
25242         var ddText = this.disabledDatesText;
25243         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25244         var ddaysText = this.disabledDaysText;
25245         var format = this.format;
25246
25247         var setCellClass = function(cal, cell){
25248             cell.title = "";
25249             var t = d.getTime();
25250             cell.firstChild.dateValue = t;
25251             if(t == today){
25252                 cell.className += " x-date-today";
25253                 cell.title = cal.todayText;
25254             }
25255             if(t == sel){
25256                 cell.className += " x-date-selected";
25257                 setTimeout(function(){
25258                     try{cell.firstChild.focus();}catch(e){}
25259                 }, 50);
25260             }
25261             // disabling
25262             if(t < min) {
25263                 cell.className = " x-date-disabled";
25264                 cell.title = cal.minText;
25265                 return;
25266             }
25267             if(t > max) {
25268                 cell.className = " x-date-disabled";
25269                 cell.title = cal.maxText;
25270                 return;
25271             }
25272             if(ddays){
25273                 if(ddays.indexOf(d.getDay()) != -1){
25274                     cell.title = ddaysText;
25275                     cell.className = " x-date-disabled";
25276                 }
25277             }
25278             if(ddMatch && format){
25279                 var fvalue = d.dateFormat(format);
25280                 if(ddMatch.test(fvalue)){
25281                     cell.title = ddText.replace("%0", fvalue);
25282                     cell.className = " x-date-disabled";
25283                 }
25284             }
25285         };
25286
25287         var i = 0;
25288         for(; i < startingPos; i++) {
25289             textEls[i].innerHTML = (++prevStart);
25290             d.setDate(d.getDate()+1);
25291             cells[i].className = "x-date-prevday";
25292             setCellClass(this, cells[i]);
25293         }
25294         for(; i < days; i++){
25295             intDay = i - startingPos + 1;
25296             textEls[i].innerHTML = (intDay);
25297             d.setDate(d.getDate()+1);
25298             cells[i].className = "x-date-active";
25299             setCellClass(this, cells[i]);
25300         }
25301         var extraDays = 0;
25302         for(; i < 42; i++) {
25303              textEls[i].innerHTML = (++extraDays);
25304              d.setDate(d.getDate()+1);
25305              cells[i].className = "x-date-nextday";
25306              setCellClass(this, cells[i]);
25307         }
25308
25309         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25310         this.fireEvent('monthchange', this, date);
25311         
25312         if(!this.internalRender){
25313             var main = this.el.dom.firstChild;
25314             var w = main.offsetWidth;
25315             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25316             Roo.fly(main).setWidth(w);
25317             this.internalRender = true;
25318             // opera does not respect the auto grow header center column
25319             // then, after it gets a width opera refuses to recalculate
25320             // without a second pass
25321             if(Roo.isOpera && !this.secondPass){
25322                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25323                 this.secondPass = true;
25324                 this.update.defer(10, this, [date]);
25325             }
25326         }
25327         
25328         
25329     }
25330 });        /*
25331  * Based on:
25332  * Ext JS Library 1.1.1
25333  * Copyright(c) 2006-2007, Ext JS, LLC.
25334  *
25335  * Originally Released Under LGPL - original licence link has changed is not relivant.
25336  *
25337  * Fork - LGPL
25338  * <script type="text/javascript">
25339  */
25340 /**
25341  * @class Roo.TabPanel
25342  * @extends Roo.util.Observable
25343  * A lightweight tab container.
25344  * <br><br>
25345  * Usage:
25346  * <pre><code>
25347 // basic tabs 1, built from existing content
25348 var tabs = new Roo.TabPanel("tabs1");
25349 tabs.addTab("script", "View Script");
25350 tabs.addTab("markup", "View Markup");
25351 tabs.activate("script");
25352
25353 // more advanced tabs, built from javascript
25354 var jtabs = new Roo.TabPanel("jtabs");
25355 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25356
25357 // set up the UpdateManager
25358 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25359 var updater = tab2.getUpdateManager();
25360 updater.setDefaultUrl("ajax1.htm");
25361 tab2.on('activate', updater.refresh, updater, true);
25362
25363 // Use setUrl for Ajax loading
25364 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25365 tab3.setUrl("ajax2.htm", null, true);
25366
25367 // Disabled tab
25368 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25369 tab4.disable();
25370
25371 jtabs.activate("jtabs-1");
25372  * </code></pre>
25373  * @constructor
25374  * Create a new TabPanel.
25375  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25376  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25377  */
25378 Roo.TabPanel = function(container, config){
25379     /**
25380     * The container element for this TabPanel.
25381     * @type Roo.Element
25382     */
25383     this.el = Roo.get(container, true);
25384     if(config){
25385         if(typeof config == "boolean"){
25386             this.tabPosition = config ? "bottom" : "top";
25387         }else{
25388             Roo.apply(this, config);
25389         }
25390     }
25391     if(this.tabPosition == "bottom"){
25392         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25393         this.el.addClass("x-tabs-bottom");
25394     }
25395     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25396     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25397     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25398     if(Roo.isIE){
25399         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25400     }
25401     if(this.tabPosition != "bottom"){
25402         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25403          * @type Roo.Element
25404          */
25405         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25406         this.el.addClass("x-tabs-top");
25407     }
25408     this.items = [];
25409
25410     this.bodyEl.setStyle("position", "relative");
25411
25412     this.active = null;
25413     this.activateDelegate = this.activate.createDelegate(this);
25414
25415     this.addEvents({
25416         /**
25417          * @event tabchange
25418          * Fires when the active tab changes
25419          * @param {Roo.TabPanel} this
25420          * @param {Roo.TabPanelItem} activePanel The new active tab
25421          */
25422         "tabchange": true,
25423         /**
25424          * @event beforetabchange
25425          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25426          * @param {Roo.TabPanel} this
25427          * @param {Object} e Set cancel to true on this object to cancel the tab change
25428          * @param {Roo.TabPanelItem} tab The tab being changed to
25429          */
25430         "beforetabchange" : true
25431     });
25432
25433     Roo.EventManager.onWindowResize(this.onResize, this);
25434     this.cpad = this.el.getPadding("lr");
25435     this.hiddenCount = 0;
25436
25437
25438     // toolbar on the tabbar support...
25439     if (this.toolbar) {
25440         var tcfg = this.toolbar;
25441         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25442         this.toolbar = new Roo.Toolbar(tcfg);
25443         if (Roo.isSafari) {
25444             var tbl = tcfg.container.child('table', true);
25445             tbl.setAttribute('width', '100%');
25446         }
25447         
25448     }
25449    
25450
25451
25452     Roo.TabPanel.superclass.constructor.call(this);
25453 };
25454
25455 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25456     /*
25457      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25458      */
25459     tabPosition : "top",
25460     /*
25461      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25462      */
25463     currentTabWidth : 0,
25464     /*
25465      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25466      */
25467     minTabWidth : 40,
25468     /*
25469      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25470      */
25471     maxTabWidth : 250,
25472     /*
25473      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25474      */
25475     preferredTabWidth : 175,
25476     /*
25477      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25478      */
25479     resizeTabs : false,
25480     /*
25481      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25482      */
25483     monitorResize : true,
25484     /*
25485      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25486      */
25487     toolbar : false,
25488
25489     /**
25490      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25491      * @param {String} id The id of the div to use <b>or create</b>
25492      * @param {String} text The text for the tab
25493      * @param {String} content (optional) Content to put in the TabPanelItem body
25494      * @param {Boolean} closable (optional) True to create a close icon on the tab
25495      * @return {Roo.TabPanelItem} The created TabPanelItem
25496      */
25497     addTab : function(id, text, content, closable){
25498         var item = new Roo.TabPanelItem(this, id, text, closable);
25499         this.addTabItem(item);
25500         if(content){
25501             item.setContent(content);
25502         }
25503         return item;
25504     },
25505
25506     /**
25507      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25508      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25509      * @return {Roo.TabPanelItem}
25510      */
25511     getTab : function(id){
25512         return this.items[id];
25513     },
25514
25515     /**
25516      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25517      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25518      */
25519     hideTab : function(id){
25520         var t = this.items[id];
25521         if(!t.isHidden()){
25522            t.setHidden(true);
25523            this.hiddenCount++;
25524            this.autoSizeTabs();
25525         }
25526     },
25527
25528     /**
25529      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25530      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25531      */
25532     unhideTab : function(id){
25533         var t = this.items[id];
25534         if(t.isHidden()){
25535            t.setHidden(false);
25536            this.hiddenCount--;
25537            this.autoSizeTabs();
25538         }
25539     },
25540
25541     /**
25542      * Adds an existing {@link Roo.TabPanelItem}.
25543      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25544      */
25545     addTabItem : function(item){
25546         this.items[item.id] = item;
25547         this.items.push(item);
25548         if(this.resizeTabs){
25549            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25550            this.autoSizeTabs();
25551         }else{
25552             item.autoSize();
25553         }
25554     },
25555
25556     /**
25557      * Removes a {@link Roo.TabPanelItem}.
25558      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25559      */
25560     removeTab : function(id){
25561         var items = this.items;
25562         var tab = items[id];
25563         if(!tab) { return; }
25564         var index = items.indexOf(tab);
25565         if(this.active == tab && items.length > 1){
25566             var newTab = this.getNextAvailable(index);
25567             if(newTab) {
25568                 newTab.activate();
25569             }
25570         }
25571         this.stripEl.dom.removeChild(tab.pnode.dom);
25572         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25573             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25574         }
25575         items.splice(index, 1);
25576         delete this.items[tab.id];
25577         tab.fireEvent("close", tab);
25578         tab.purgeListeners();
25579         this.autoSizeTabs();
25580     },
25581
25582     getNextAvailable : function(start){
25583         var items = this.items;
25584         var index = start;
25585         // look for a next tab that will slide over to
25586         // replace the one being removed
25587         while(index < items.length){
25588             var item = items[++index];
25589             if(item && !item.isHidden()){
25590                 return item;
25591             }
25592         }
25593         // if one isn't found select the previous tab (on the left)
25594         index = start;
25595         while(index >= 0){
25596             var item = items[--index];
25597             if(item && !item.isHidden()){
25598                 return item;
25599             }
25600         }
25601         return null;
25602     },
25603
25604     /**
25605      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25606      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25607      */
25608     disableTab : function(id){
25609         var tab = this.items[id];
25610         if(tab && this.active != tab){
25611             tab.disable();
25612         }
25613     },
25614
25615     /**
25616      * Enables a {@link Roo.TabPanelItem} that is disabled.
25617      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25618      */
25619     enableTab : function(id){
25620         var tab = this.items[id];
25621         tab.enable();
25622     },
25623
25624     /**
25625      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25626      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25627      * @return {Roo.TabPanelItem} The TabPanelItem.
25628      */
25629     activate : function(id){
25630         var tab = this.items[id];
25631         if(!tab){
25632             return null;
25633         }
25634         if(tab == this.active || tab.disabled){
25635             return tab;
25636         }
25637         var e = {};
25638         this.fireEvent("beforetabchange", this, e, tab);
25639         if(e.cancel !== true && !tab.disabled){
25640             if(this.active){
25641                 this.active.hide();
25642             }
25643             this.active = this.items[id];
25644             this.active.show();
25645             this.fireEvent("tabchange", this, this.active);
25646         }
25647         return tab;
25648     },
25649
25650     /**
25651      * Gets the active {@link Roo.TabPanelItem}.
25652      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25653      */
25654     getActiveTab : function(){
25655         return this.active;
25656     },
25657
25658     /**
25659      * Updates the tab body element to fit the height of the container element
25660      * for overflow scrolling
25661      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25662      */
25663     syncHeight : function(targetHeight){
25664         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25665         var bm = this.bodyEl.getMargins();
25666         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25667         this.bodyEl.setHeight(newHeight);
25668         return newHeight;
25669     },
25670
25671     onResize : function(){
25672         if(this.monitorResize){
25673             this.autoSizeTabs();
25674         }
25675     },
25676
25677     /**
25678      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25679      */
25680     beginUpdate : function(){
25681         this.updating = true;
25682     },
25683
25684     /**
25685      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25686      */
25687     endUpdate : function(){
25688         this.updating = false;
25689         this.autoSizeTabs();
25690     },
25691
25692     /**
25693      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25694      */
25695     autoSizeTabs : function(){
25696         var count = this.items.length;
25697         var vcount = count - this.hiddenCount;
25698         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25699         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25700         var availWidth = Math.floor(w / vcount);
25701         var b = this.stripBody;
25702         if(b.getWidth() > w){
25703             var tabs = this.items;
25704             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25705             if(availWidth < this.minTabWidth){
25706                 /*if(!this.sleft){    // incomplete scrolling code
25707                     this.createScrollButtons();
25708                 }
25709                 this.showScroll();
25710                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25711             }
25712         }else{
25713             if(this.currentTabWidth < this.preferredTabWidth){
25714                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25715             }
25716         }
25717     },
25718
25719     /**
25720      * Returns the number of tabs in this TabPanel.
25721      * @return {Number}
25722      */
25723      getCount : function(){
25724          return this.items.length;
25725      },
25726
25727     /**
25728      * Resizes all the tabs to the passed width
25729      * @param {Number} The new width
25730      */
25731     setTabWidth : function(width){
25732         this.currentTabWidth = width;
25733         for(var i = 0, len = this.items.length; i < len; i++) {
25734                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25735         }
25736     },
25737
25738     /**
25739      * Destroys this TabPanel
25740      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25741      */
25742     destroy : function(removeEl){
25743         Roo.EventManager.removeResizeListener(this.onResize, this);
25744         for(var i = 0, len = this.items.length; i < len; i++){
25745             this.items[i].purgeListeners();
25746         }
25747         if(removeEl === true){
25748             this.el.update("");
25749             this.el.remove();
25750         }
25751     }
25752 });
25753
25754 /**
25755  * @class Roo.TabPanelItem
25756  * @extends Roo.util.Observable
25757  * Represents an individual item (tab plus body) in a TabPanel.
25758  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25759  * @param {String} id The id of this TabPanelItem
25760  * @param {String} text The text for the tab of this TabPanelItem
25761  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25762  */
25763 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25764     /**
25765      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25766      * @type Roo.TabPanel
25767      */
25768     this.tabPanel = tabPanel;
25769     /**
25770      * The id for this TabPanelItem
25771      * @type String
25772      */
25773     this.id = id;
25774     /** @private */
25775     this.disabled = false;
25776     /** @private */
25777     this.text = text;
25778     /** @private */
25779     this.loaded = false;
25780     this.closable = closable;
25781
25782     /**
25783      * The body element for this TabPanelItem.
25784      * @type Roo.Element
25785      */
25786     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25787     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25788     this.bodyEl.setStyle("display", "block");
25789     this.bodyEl.setStyle("zoom", "1");
25790     this.hideAction();
25791
25792     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25793     /** @private */
25794     this.el = Roo.get(els.el, true);
25795     this.inner = Roo.get(els.inner, true);
25796     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25797     this.pnode = Roo.get(els.el.parentNode, true);
25798     this.el.on("mousedown", this.onTabMouseDown, this);
25799     this.el.on("click", this.onTabClick, this);
25800     /** @private */
25801     if(closable){
25802         var c = Roo.get(els.close, true);
25803         c.dom.title = this.closeText;
25804         c.addClassOnOver("close-over");
25805         c.on("click", this.closeClick, this);
25806      }
25807
25808     this.addEvents({
25809          /**
25810          * @event activate
25811          * Fires when this tab becomes the active tab.
25812          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25813          * @param {Roo.TabPanelItem} this
25814          */
25815         "activate": true,
25816         /**
25817          * @event beforeclose
25818          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25819          * @param {Roo.TabPanelItem} this
25820          * @param {Object} e Set cancel to true on this object to cancel the close.
25821          */
25822         "beforeclose": true,
25823         /**
25824          * @event close
25825          * Fires when this tab is closed.
25826          * @param {Roo.TabPanelItem} this
25827          */
25828          "close": true,
25829         /**
25830          * @event deactivate
25831          * Fires when this tab is no longer the active tab.
25832          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25833          * @param {Roo.TabPanelItem} this
25834          */
25835          "deactivate" : true
25836     });
25837     this.hidden = false;
25838
25839     Roo.TabPanelItem.superclass.constructor.call(this);
25840 };
25841
25842 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25843     purgeListeners : function(){
25844        Roo.util.Observable.prototype.purgeListeners.call(this);
25845        this.el.removeAllListeners();
25846     },
25847     /**
25848      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25849      */
25850     show : function(){
25851         this.pnode.addClass("on");
25852         this.showAction();
25853         if(Roo.isOpera){
25854             this.tabPanel.stripWrap.repaint();
25855         }
25856         this.fireEvent("activate", this.tabPanel, this);
25857     },
25858
25859     /**
25860      * Returns true if this tab is the active tab.
25861      * @return {Boolean}
25862      */
25863     isActive : function(){
25864         return this.tabPanel.getActiveTab() == this;
25865     },
25866
25867     /**
25868      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25869      */
25870     hide : function(){
25871         this.pnode.removeClass("on");
25872         this.hideAction();
25873         this.fireEvent("deactivate", this.tabPanel, this);
25874     },
25875
25876     hideAction : function(){
25877         this.bodyEl.hide();
25878         this.bodyEl.setStyle("position", "absolute");
25879         this.bodyEl.setLeft("-20000px");
25880         this.bodyEl.setTop("-20000px");
25881     },
25882
25883     showAction : function(){
25884         this.bodyEl.setStyle("position", "relative");
25885         this.bodyEl.setTop("");
25886         this.bodyEl.setLeft("");
25887         this.bodyEl.show();
25888     },
25889
25890     /**
25891      * Set the tooltip for the tab.
25892      * @param {String} tooltip The tab's tooltip
25893      */
25894     setTooltip : function(text){
25895         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25896             this.textEl.dom.qtip = text;
25897             this.textEl.dom.removeAttribute('title');
25898         }else{
25899             this.textEl.dom.title = text;
25900         }
25901     },
25902
25903     onTabClick : function(e){
25904         e.preventDefault();
25905         this.tabPanel.activate(this.id);
25906     },
25907
25908     onTabMouseDown : function(e){
25909         e.preventDefault();
25910         this.tabPanel.activate(this.id);
25911     },
25912
25913     getWidth : function(){
25914         return this.inner.getWidth();
25915     },
25916
25917     setWidth : function(width){
25918         var iwidth = width - this.pnode.getPadding("lr");
25919         this.inner.setWidth(iwidth);
25920         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25921         this.pnode.setWidth(width);
25922     },
25923
25924     /**
25925      * Show or hide the tab
25926      * @param {Boolean} hidden True to hide or false to show.
25927      */
25928     setHidden : function(hidden){
25929         this.hidden = hidden;
25930         this.pnode.setStyle("display", hidden ? "none" : "");
25931     },
25932
25933     /**
25934      * Returns true if this tab is "hidden"
25935      * @return {Boolean}
25936      */
25937     isHidden : function(){
25938         return this.hidden;
25939     },
25940
25941     /**
25942      * Returns the text for this tab
25943      * @return {String}
25944      */
25945     getText : function(){
25946         return this.text;
25947     },
25948
25949     autoSize : function(){
25950         //this.el.beginMeasure();
25951         this.textEl.setWidth(1);
25952         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25953         //this.el.endMeasure();
25954     },
25955
25956     /**
25957      * Sets the text for the tab (Note: this also sets the tooltip text)
25958      * @param {String} text The tab's text and tooltip
25959      */
25960     setText : function(text){
25961         this.text = text;
25962         this.textEl.update(text);
25963         this.setTooltip(text);
25964         if(!this.tabPanel.resizeTabs){
25965             this.autoSize();
25966         }
25967     },
25968     /**
25969      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25970      */
25971     activate : function(){
25972         this.tabPanel.activate(this.id);
25973     },
25974
25975     /**
25976      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25977      */
25978     disable : function(){
25979         if(this.tabPanel.active != this){
25980             this.disabled = true;
25981             this.pnode.addClass("disabled");
25982         }
25983     },
25984
25985     /**
25986      * Enables this TabPanelItem if it was previously disabled.
25987      */
25988     enable : function(){
25989         this.disabled = false;
25990         this.pnode.removeClass("disabled");
25991     },
25992
25993     /**
25994      * Sets the content for this TabPanelItem.
25995      * @param {String} content The content
25996      * @param {Boolean} loadScripts true to look for and load scripts
25997      */
25998     setContent : function(content, loadScripts){
25999         this.bodyEl.update(content, loadScripts);
26000     },
26001
26002     /**
26003      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26004      * @return {Roo.UpdateManager} The UpdateManager
26005      */
26006     getUpdateManager : function(){
26007         return this.bodyEl.getUpdateManager();
26008     },
26009
26010     /**
26011      * Set a URL to be used to load the content for this TabPanelItem.
26012      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26013      * @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)
26014      * @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)
26015      * @return {Roo.UpdateManager} The UpdateManager
26016      */
26017     setUrl : function(url, params, loadOnce){
26018         if(this.refreshDelegate){
26019             this.un('activate', this.refreshDelegate);
26020         }
26021         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26022         this.on("activate", this.refreshDelegate);
26023         return this.bodyEl.getUpdateManager();
26024     },
26025
26026     /** @private */
26027     _handleRefresh : function(url, params, loadOnce){
26028         if(!loadOnce || !this.loaded){
26029             var updater = this.bodyEl.getUpdateManager();
26030             updater.update(url, params, this._setLoaded.createDelegate(this));
26031         }
26032     },
26033
26034     /**
26035      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26036      *   Will fail silently if the setUrl method has not been called.
26037      *   This does not activate the panel, just updates its content.
26038      */
26039     refresh : function(){
26040         if(this.refreshDelegate){
26041            this.loaded = false;
26042            this.refreshDelegate();
26043         }
26044     },
26045
26046     /** @private */
26047     _setLoaded : function(){
26048         this.loaded = true;
26049     },
26050
26051     /** @private */
26052     closeClick : function(e){
26053         var o = {};
26054         e.stopEvent();
26055         this.fireEvent("beforeclose", this, o);
26056         if(o.cancel !== true){
26057             this.tabPanel.removeTab(this.id);
26058         }
26059     },
26060     /**
26061      * The text displayed in the tooltip for the close icon.
26062      * @type String
26063      */
26064     closeText : "Close this tab"
26065 });
26066
26067 /** @private */
26068 Roo.TabPanel.prototype.createStrip = function(container){
26069     var strip = document.createElement("div");
26070     strip.className = "x-tabs-wrap";
26071     container.appendChild(strip);
26072     return strip;
26073 };
26074 /** @private */
26075 Roo.TabPanel.prototype.createStripList = function(strip){
26076     // div wrapper for retard IE
26077     // returns the "tr" element.
26078     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26079         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26080         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26081     return strip.firstChild.firstChild.firstChild.firstChild;
26082 };
26083 /** @private */
26084 Roo.TabPanel.prototype.createBody = function(container){
26085     var body = document.createElement("div");
26086     Roo.id(body, "tab-body");
26087     Roo.fly(body).addClass("x-tabs-body");
26088     container.appendChild(body);
26089     return body;
26090 };
26091 /** @private */
26092 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26093     var body = Roo.getDom(id);
26094     if(!body){
26095         body = document.createElement("div");
26096         body.id = id;
26097     }
26098     Roo.fly(body).addClass("x-tabs-item-body");
26099     bodyEl.insertBefore(body, bodyEl.firstChild);
26100     return body;
26101 };
26102 /** @private */
26103 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26104     var td = document.createElement("td");
26105     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26106     //stripEl.appendChild(td);
26107     if(closable){
26108         td.className = "x-tabs-closable";
26109         if(!this.closeTpl){
26110             this.closeTpl = new Roo.Template(
26111                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26112                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26113                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26114             );
26115         }
26116         var el = this.closeTpl.overwrite(td, {"text": text});
26117         var close = el.getElementsByTagName("div")[0];
26118         var inner = el.getElementsByTagName("em")[0];
26119         return {"el": el, "close": close, "inner": inner};
26120     } else {
26121         if(!this.tabTpl){
26122             this.tabTpl = new Roo.Template(
26123                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26124                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26125             );
26126         }
26127         var el = this.tabTpl.overwrite(td, {"text": text});
26128         var inner = el.getElementsByTagName("em")[0];
26129         return {"el": el, "inner": inner};
26130     }
26131 };/*
26132  * Based on:
26133  * Ext JS Library 1.1.1
26134  * Copyright(c) 2006-2007, Ext JS, LLC.
26135  *
26136  * Originally Released Under LGPL - original licence link has changed is not relivant.
26137  *
26138  * Fork - LGPL
26139  * <script type="text/javascript">
26140  */
26141
26142 /**
26143  * @class Roo.Button
26144  * @extends Roo.util.Observable
26145  * Simple Button class
26146  * @cfg {String} text The button text
26147  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26148  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26149  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26150  * @cfg {Object} scope The scope of the handler
26151  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26152  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26153  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26154  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26155  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26156  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26157    applies if enableToggle = true)
26158  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26159  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26160   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26161  * @constructor
26162  * Create a new button
26163  * @param {Object} config The config object
26164  */
26165 Roo.Button = function(renderTo, config)
26166 {
26167     if (!config) {
26168         config = renderTo;
26169         renderTo = config.renderTo || false;
26170     }
26171     
26172     Roo.apply(this, config);
26173     this.addEvents({
26174         /**
26175              * @event click
26176              * Fires when this button is clicked
26177              * @param {Button} this
26178              * @param {EventObject} e The click event
26179              */
26180             "click" : true,
26181         /**
26182              * @event toggle
26183              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26184              * @param {Button} this
26185              * @param {Boolean} pressed
26186              */
26187             "toggle" : true,
26188         /**
26189              * @event mouseover
26190              * Fires when the mouse hovers over the button
26191              * @param {Button} this
26192              * @param {Event} e The event object
26193              */
26194         'mouseover' : true,
26195         /**
26196              * @event mouseout
26197              * Fires when the mouse exits the button
26198              * @param {Button} this
26199              * @param {Event} e The event object
26200              */
26201         'mouseout': true,
26202          /**
26203              * @event render
26204              * Fires when the button is rendered
26205              * @param {Button} this
26206              */
26207         'render': true
26208     });
26209     if(this.menu){
26210         this.menu = Roo.menu.MenuMgr.get(this.menu);
26211     }
26212     // register listeners first!!  - so render can be captured..
26213     Roo.util.Observable.call(this);
26214     if(renderTo){
26215         this.render(renderTo);
26216     }
26217     
26218   
26219 };
26220
26221 Roo.extend(Roo.Button, Roo.util.Observable, {
26222     /**
26223      * 
26224      */
26225     
26226     /**
26227      * Read-only. True if this button is hidden
26228      * @type Boolean
26229      */
26230     hidden : false,
26231     /**
26232      * Read-only. True if this button is disabled
26233      * @type Boolean
26234      */
26235     disabled : false,
26236     /**
26237      * Read-only. True if this button is pressed (only if enableToggle = true)
26238      * @type Boolean
26239      */
26240     pressed : false,
26241
26242     /**
26243      * @cfg {Number} tabIndex 
26244      * The DOM tabIndex for this button (defaults to undefined)
26245      */
26246     tabIndex : undefined,
26247
26248     /**
26249      * @cfg {Boolean} enableToggle
26250      * True to enable pressed/not pressed toggling (defaults to false)
26251      */
26252     enableToggle: false,
26253     /**
26254      * @cfg {Mixed} menu
26255      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26256      */
26257     menu : undefined,
26258     /**
26259      * @cfg {String} menuAlign
26260      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26261      */
26262     menuAlign : "tl-bl?",
26263
26264     /**
26265      * @cfg {String} iconCls
26266      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26267      */
26268     iconCls : undefined,
26269     /**
26270      * @cfg {String} type
26271      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26272      */
26273     type : 'button',
26274
26275     // private
26276     menuClassTarget: 'tr',
26277
26278     /**
26279      * @cfg {String} clickEvent
26280      * The type of event to map to the button's event handler (defaults to 'click')
26281      */
26282     clickEvent : 'click',
26283
26284     /**
26285      * @cfg {Boolean} handleMouseEvents
26286      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26287      */
26288     handleMouseEvents : true,
26289
26290     /**
26291      * @cfg {String} tooltipType
26292      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26293      */
26294     tooltipType : 'qtip',
26295
26296     /**
26297      * @cfg {String} cls
26298      * A CSS class to apply to the button's main element.
26299      */
26300     
26301     /**
26302      * @cfg {Roo.Template} template (Optional)
26303      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26304      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26305      * require code modifications if required elements (e.g. a button) aren't present.
26306      */
26307
26308     // private
26309     render : function(renderTo){
26310         var btn;
26311         if(this.hideParent){
26312             this.parentEl = Roo.get(renderTo);
26313         }
26314         if(!this.dhconfig){
26315             if(!this.template){
26316                 if(!Roo.Button.buttonTemplate){
26317                     // hideous table template
26318                     Roo.Button.buttonTemplate = new Roo.Template(
26319                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26320                         '<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>',
26321                         "</tr></tbody></table>");
26322                 }
26323                 this.template = Roo.Button.buttonTemplate;
26324             }
26325             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26326             var btnEl = btn.child("button:first");
26327             btnEl.on('focus', this.onFocus, this);
26328             btnEl.on('blur', this.onBlur, this);
26329             if(this.cls){
26330                 btn.addClass(this.cls);
26331             }
26332             if(this.icon){
26333                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26334             }
26335             if(this.iconCls){
26336                 btnEl.addClass(this.iconCls);
26337                 if(!this.cls){
26338                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26339                 }
26340             }
26341             if(this.tabIndex !== undefined){
26342                 btnEl.dom.tabIndex = this.tabIndex;
26343             }
26344             if(this.tooltip){
26345                 if(typeof this.tooltip == 'object'){
26346                     Roo.QuickTips.tips(Roo.apply({
26347                           target: btnEl.id
26348                     }, this.tooltip));
26349                 } else {
26350                     btnEl.dom[this.tooltipType] = this.tooltip;
26351                 }
26352             }
26353         }else{
26354             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26355         }
26356         this.el = btn;
26357         if(this.id){
26358             this.el.dom.id = this.el.id = this.id;
26359         }
26360         if(this.menu){
26361             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26362             this.menu.on("show", this.onMenuShow, this);
26363             this.menu.on("hide", this.onMenuHide, this);
26364         }
26365         btn.addClass("x-btn");
26366         if(Roo.isIE && !Roo.isIE7){
26367             this.autoWidth.defer(1, this);
26368         }else{
26369             this.autoWidth();
26370         }
26371         if(this.handleMouseEvents){
26372             btn.on("mouseover", this.onMouseOver, this);
26373             btn.on("mouseout", this.onMouseOut, this);
26374             btn.on("mousedown", this.onMouseDown, this);
26375         }
26376         btn.on(this.clickEvent, this.onClick, this);
26377         //btn.on("mouseup", this.onMouseUp, this);
26378         if(this.hidden){
26379             this.hide();
26380         }
26381         if(this.disabled){
26382             this.disable();
26383         }
26384         Roo.ButtonToggleMgr.register(this);
26385         if(this.pressed){
26386             this.el.addClass("x-btn-pressed");
26387         }
26388         if(this.repeat){
26389             var repeater = new Roo.util.ClickRepeater(btn,
26390                 typeof this.repeat == "object" ? this.repeat : {}
26391             );
26392             repeater.on("click", this.onClick,  this);
26393         }
26394         
26395         this.fireEvent('render', this);
26396         
26397     },
26398     /**
26399      * Returns the button's underlying element
26400      * @return {Roo.Element} The element
26401      */
26402     getEl : function(){
26403         return this.el;  
26404     },
26405     
26406     /**
26407      * Destroys this Button and removes any listeners.
26408      */
26409     destroy : function(){
26410         Roo.ButtonToggleMgr.unregister(this);
26411         this.el.removeAllListeners();
26412         this.purgeListeners();
26413         this.el.remove();
26414     },
26415
26416     // private
26417     autoWidth : function(){
26418         if(this.el){
26419             this.el.setWidth("auto");
26420             if(Roo.isIE7 && Roo.isStrict){
26421                 var ib = this.el.child('button');
26422                 if(ib && ib.getWidth() > 20){
26423                     ib.clip();
26424                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26425                 }
26426             }
26427             if(this.minWidth){
26428                 if(this.hidden){
26429                     this.el.beginMeasure();
26430                 }
26431                 if(this.el.getWidth() < this.minWidth){
26432                     this.el.setWidth(this.minWidth);
26433                 }
26434                 if(this.hidden){
26435                     this.el.endMeasure();
26436                 }
26437             }
26438         }
26439     },
26440
26441     /**
26442      * Assigns this button's click handler
26443      * @param {Function} handler The function to call when the button is clicked
26444      * @param {Object} scope (optional) Scope for the function passed in
26445      */
26446     setHandler : function(handler, scope){
26447         this.handler = handler;
26448         this.scope = scope;  
26449     },
26450     
26451     /**
26452      * Sets this button's text
26453      * @param {String} text The button text
26454      */
26455     setText : function(text){
26456         this.text = text;
26457         if(this.el){
26458             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26459         }
26460         this.autoWidth();
26461     },
26462     
26463     /**
26464      * Gets the text for this button
26465      * @return {String} The button text
26466      */
26467     getText : function(){
26468         return this.text;  
26469     },
26470     
26471     /**
26472      * Show this button
26473      */
26474     show: function(){
26475         this.hidden = false;
26476         if(this.el){
26477             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26478         }
26479     },
26480     
26481     /**
26482      * Hide this button
26483      */
26484     hide: function(){
26485         this.hidden = true;
26486         if(this.el){
26487             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26488         }
26489     },
26490     
26491     /**
26492      * Convenience function for boolean show/hide
26493      * @param {Boolean} visible True to show, false to hide
26494      */
26495     setVisible: function(visible){
26496         if(visible) {
26497             this.show();
26498         }else{
26499             this.hide();
26500         }
26501     },
26502     
26503     /**
26504      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26505      * @param {Boolean} state (optional) Force a particular state
26506      */
26507     toggle : function(state){
26508         state = state === undefined ? !this.pressed : state;
26509         if(state != this.pressed){
26510             if(state){
26511                 this.el.addClass("x-btn-pressed");
26512                 this.pressed = true;
26513                 this.fireEvent("toggle", this, true);
26514             }else{
26515                 this.el.removeClass("x-btn-pressed");
26516                 this.pressed = false;
26517                 this.fireEvent("toggle", this, false);
26518             }
26519             if(this.toggleHandler){
26520                 this.toggleHandler.call(this.scope || this, this, state);
26521             }
26522         }
26523     },
26524     
26525     /**
26526      * Focus the button
26527      */
26528     focus : function(){
26529         this.el.child('button:first').focus();
26530     },
26531     
26532     /**
26533      * Disable this button
26534      */
26535     disable : function(){
26536         if(this.el){
26537             this.el.addClass("x-btn-disabled");
26538         }
26539         this.disabled = true;
26540     },
26541     
26542     /**
26543      * Enable this button
26544      */
26545     enable : function(){
26546         if(this.el){
26547             this.el.removeClass("x-btn-disabled");
26548         }
26549         this.disabled = false;
26550     },
26551
26552     /**
26553      * Convenience function for boolean enable/disable
26554      * @param {Boolean} enabled True to enable, false to disable
26555      */
26556     setDisabled : function(v){
26557         this[v !== true ? "enable" : "disable"]();
26558     },
26559
26560     // private
26561     onClick : function(e){
26562         if(e){
26563             e.preventDefault();
26564         }
26565         if(e.button != 0){
26566             return;
26567         }
26568         if(!this.disabled){
26569             if(this.enableToggle){
26570                 this.toggle();
26571             }
26572             if(this.menu && !this.menu.isVisible()){
26573                 this.menu.show(this.el, this.menuAlign);
26574             }
26575             this.fireEvent("click", this, e);
26576             if(this.handler){
26577                 this.el.removeClass("x-btn-over");
26578                 this.handler.call(this.scope || this, this, e);
26579             }
26580         }
26581     },
26582     // private
26583     onMouseOver : function(e){
26584         if(!this.disabled){
26585             this.el.addClass("x-btn-over");
26586             this.fireEvent('mouseover', this, e);
26587         }
26588     },
26589     // private
26590     onMouseOut : function(e){
26591         if(!e.within(this.el,  true)){
26592             this.el.removeClass("x-btn-over");
26593             this.fireEvent('mouseout', this, e);
26594         }
26595     },
26596     // private
26597     onFocus : function(e){
26598         if(!this.disabled){
26599             this.el.addClass("x-btn-focus");
26600         }
26601     },
26602     // private
26603     onBlur : function(e){
26604         this.el.removeClass("x-btn-focus");
26605     },
26606     // private
26607     onMouseDown : function(e){
26608         if(!this.disabled && e.button == 0){
26609             this.el.addClass("x-btn-click");
26610             Roo.get(document).on('mouseup', this.onMouseUp, this);
26611         }
26612     },
26613     // private
26614     onMouseUp : function(e){
26615         if(e.button == 0){
26616             this.el.removeClass("x-btn-click");
26617             Roo.get(document).un('mouseup', this.onMouseUp, this);
26618         }
26619     },
26620     // private
26621     onMenuShow : function(e){
26622         this.el.addClass("x-btn-menu-active");
26623     },
26624     // private
26625     onMenuHide : function(e){
26626         this.el.removeClass("x-btn-menu-active");
26627     }   
26628 });
26629
26630 // Private utility class used by Button
26631 Roo.ButtonToggleMgr = function(){
26632    var groups = {};
26633    
26634    function toggleGroup(btn, state){
26635        if(state){
26636            var g = groups[btn.toggleGroup];
26637            for(var i = 0, l = g.length; i < l; i++){
26638                if(g[i] != btn){
26639                    g[i].toggle(false);
26640                }
26641            }
26642        }
26643    }
26644    
26645    return {
26646        register : function(btn){
26647            if(!btn.toggleGroup){
26648                return;
26649            }
26650            var g = groups[btn.toggleGroup];
26651            if(!g){
26652                g = groups[btn.toggleGroup] = [];
26653            }
26654            g.push(btn);
26655            btn.on("toggle", toggleGroup);
26656        },
26657        
26658        unregister : function(btn){
26659            if(!btn.toggleGroup){
26660                return;
26661            }
26662            var g = groups[btn.toggleGroup];
26663            if(g){
26664                g.remove(btn);
26665                btn.un("toggle", toggleGroup);
26666            }
26667        }
26668    };
26669 }();/*
26670  * Based on:
26671  * Ext JS Library 1.1.1
26672  * Copyright(c) 2006-2007, Ext JS, LLC.
26673  *
26674  * Originally Released Under LGPL - original licence link has changed is not relivant.
26675  *
26676  * Fork - LGPL
26677  * <script type="text/javascript">
26678  */
26679  
26680 /**
26681  * @class Roo.SplitButton
26682  * @extends Roo.Button
26683  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26684  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26685  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26686  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26687  * @cfg {String} arrowTooltip The title attribute of the arrow
26688  * @constructor
26689  * Create a new menu button
26690  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26691  * @param {Object} config The config object
26692  */
26693 Roo.SplitButton = function(renderTo, config){
26694     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26695     /**
26696      * @event arrowclick
26697      * Fires when this button's arrow is clicked
26698      * @param {SplitButton} this
26699      * @param {EventObject} e The click event
26700      */
26701     this.addEvents({"arrowclick":true});
26702 };
26703
26704 Roo.extend(Roo.SplitButton, Roo.Button, {
26705     render : function(renderTo){
26706         // this is one sweet looking template!
26707         var tpl = new Roo.Template(
26708             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26709             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26710             '<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>',
26711             "</tbody></table></td><td>",
26712             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26713             '<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>',
26714             "</tbody></table></td></tr></table>"
26715         );
26716         var btn = tpl.append(renderTo, [this.text, this.type], true);
26717         var btnEl = btn.child("button");
26718         if(this.cls){
26719             btn.addClass(this.cls);
26720         }
26721         if(this.icon){
26722             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26723         }
26724         if(this.iconCls){
26725             btnEl.addClass(this.iconCls);
26726             if(!this.cls){
26727                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26728             }
26729         }
26730         this.el = btn;
26731         if(this.handleMouseEvents){
26732             btn.on("mouseover", this.onMouseOver, this);
26733             btn.on("mouseout", this.onMouseOut, this);
26734             btn.on("mousedown", this.onMouseDown, this);
26735             btn.on("mouseup", this.onMouseUp, this);
26736         }
26737         btn.on(this.clickEvent, this.onClick, this);
26738         if(this.tooltip){
26739             if(typeof this.tooltip == 'object'){
26740                 Roo.QuickTips.tips(Roo.apply({
26741                       target: btnEl.id
26742                 }, this.tooltip));
26743             } else {
26744                 btnEl.dom[this.tooltipType] = this.tooltip;
26745             }
26746         }
26747         if(this.arrowTooltip){
26748             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26749         }
26750         if(this.hidden){
26751             this.hide();
26752         }
26753         if(this.disabled){
26754             this.disable();
26755         }
26756         if(this.pressed){
26757             this.el.addClass("x-btn-pressed");
26758         }
26759         if(Roo.isIE && !Roo.isIE7){
26760             this.autoWidth.defer(1, this);
26761         }else{
26762             this.autoWidth();
26763         }
26764         if(this.menu){
26765             this.menu.on("show", this.onMenuShow, this);
26766             this.menu.on("hide", this.onMenuHide, this);
26767         }
26768         this.fireEvent('render', this);
26769     },
26770
26771     // private
26772     autoWidth : function(){
26773         if(this.el){
26774             var tbl = this.el.child("table:first");
26775             var tbl2 = this.el.child("table:last");
26776             this.el.setWidth("auto");
26777             tbl.setWidth("auto");
26778             if(Roo.isIE7 && Roo.isStrict){
26779                 var ib = this.el.child('button:first');
26780                 if(ib && ib.getWidth() > 20){
26781                     ib.clip();
26782                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26783                 }
26784             }
26785             if(this.minWidth){
26786                 if(this.hidden){
26787                     this.el.beginMeasure();
26788                 }
26789                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26790                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26791                 }
26792                 if(this.hidden){
26793                     this.el.endMeasure();
26794                 }
26795             }
26796             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26797         } 
26798     },
26799     /**
26800      * Sets this button's click handler
26801      * @param {Function} handler The function to call when the button is clicked
26802      * @param {Object} scope (optional) Scope for the function passed above
26803      */
26804     setHandler : function(handler, scope){
26805         this.handler = handler;
26806         this.scope = scope;  
26807     },
26808     
26809     /**
26810      * Sets this button's arrow click handler
26811      * @param {Function} handler The function to call when the arrow is clicked
26812      * @param {Object} scope (optional) Scope for the function passed above
26813      */
26814     setArrowHandler : function(handler, scope){
26815         this.arrowHandler = handler;
26816         this.scope = scope;  
26817     },
26818     
26819     /**
26820      * Focus the button
26821      */
26822     focus : function(){
26823         if(this.el){
26824             this.el.child("button:first").focus();
26825         }
26826     },
26827
26828     // private
26829     onClick : function(e){
26830         e.preventDefault();
26831         if(!this.disabled){
26832             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26833                 if(this.menu && !this.menu.isVisible()){
26834                     this.menu.show(this.el, this.menuAlign);
26835                 }
26836                 this.fireEvent("arrowclick", this, e);
26837                 if(this.arrowHandler){
26838                     this.arrowHandler.call(this.scope || this, this, e);
26839                 }
26840             }else{
26841                 this.fireEvent("click", this, e);
26842                 if(this.handler){
26843                     this.handler.call(this.scope || this, this, e);
26844                 }
26845             }
26846         }
26847     },
26848     // private
26849     onMouseDown : function(e){
26850         if(!this.disabled){
26851             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26852         }
26853     },
26854     // private
26855     onMouseUp : function(e){
26856         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26857     }   
26858 });
26859
26860
26861 // backwards compat
26862 Roo.MenuButton = Roo.SplitButton;/*
26863  * Based on:
26864  * Ext JS Library 1.1.1
26865  * Copyright(c) 2006-2007, Ext JS, LLC.
26866  *
26867  * Originally Released Under LGPL - original licence link has changed is not relivant.
26868  *
26869  * Fork - LGPL
26870  * <script type="text/javascript">
26871  */
26872
26873 /**
26874  * @class Roo.Toolbar
26875  * Basic Toolbar class.
26876  * @constructor
26877  * Creates a new Toolbar
26878  * @param {Object} container The config object
26879  */ 
26880 Roo.Toolbar = function(container, buttons, config)
26881 {
26882     /// old consturctor format still supported..
26883     if(container instanceof Array){ // omit the container for later rendering
26884         buttons = container;
26885         config = buttons;
26886         container = null;
26887     }
26888     if (typeof(container) == 'object' && container.xtype) {
26889         config = container;
26890         container = config.container;
26891         buttons = config.buttons || []; // not really - use items!!
26892     }
26893     var xitems = [];
26894     if (config && config.items) {
26895         xitems = config.items;
26896         delete config.items;
26897     }
26898     Roo.apply(this, config);
26899     this.buttons = buttons;
26900     
26901     if(container){
26902         this.render(container);
26903     }
26904     this.xitems = xitems;
26905     Roo.each(xitems, function(b) {
26906         this.add(b);
26907     }, this);
26908     
26909 };
26910
26911 Roo.Toolbar.prototype = {
26912     /**
26913      * @cfg {Array} items
26914      * array of button configs or elements to add (will be converted to a MixedCollection)
26915      */
26916     
26917     /**
26918      * @cfg {String/HTMLElement/Element} container
26919      * The id or element that will contain the toolbar
26920      */
26921     // private
26922     render : function(ct){
26923         this.el = Roo.get(ct);
26924         if(this.cls){
26925             this.el.addClass(this.cls);
26926         }
26927         // using a table allows for vertical alignment
26928         // 100% width is needed by Safari...
26929         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26930         this.tr = this.el.child("tr", true);
26931         var autoId = 0;
26932         this.items = new Roo.util.MixedCollection(false, function(o){
26933             return o.id || ("item" + (++autoId));
26934         });
26935         if(this.buttons){
26936             this.add.apply(this, this.buttons);
26937             delete this.buttons;
26938         }
26939     },
26940
26941     /**
26942      * Adds element(s) to the toolbar -- this function takes a variable number of 
26943      * arguments of mixed type and adds them to the toolbar.
26944      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26945      * <ul>
26946      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26947      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26948      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26949      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26950      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26951      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26952      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26953      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26954      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26955      * </ul>
26956      * @param {Mixed} arg2
26957      * @param {Mixed} etc.
26958      */
26959     add : function(){
26960         var a = arguments, l = a.length;
26961         for(var i = 0; i < l; i++){
26962             this._add(a[i]);
26963         }
26964     },
26965     // private..
26966     _add : function(el) {
26967         
26968         if (el.xtype) {
26969             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26970         }
26971         
26972         if (el.applyTo){ // some kind of form field
26973             return this.addField(el);
26974         } 
26975         if (el.render){ // some kind of Toolbar.Item
26976             return this.addItem(el);
26977         }
26978         if (typeof el == "string"){ // string
26979             if(el == "separator" || el == "-"){
26980                 return this.addSeparator();
26981             }
26982             if (el == " "){
26983                 return this.addSpacer();
26984             }
26985             if(el == "->"){
26986                 return this.addFill();
26987             }
26988             return this.addText(el);
26989             
26990         }
26991         if(el.tagName){ // element
26992             return this.addElement(el);
26993         }
26994         if(typeof el == "object"){ // must be button config?
26995             return this.addButton(el);
26996         }
26997         // and now what?!?!
26998         return false;
26999         
27000     },
27001     
27002     /**
27003      * Add an Xtype element
27004      * @param {Object} xtype Xtype Object
27005      * @return {Object} created Object
27006      */
27007     addxtype : function(e){
27008         return this.add(e);  
27009     },
27010     
27011     /**
27012      * Returns the Element for this toolbar.
27013      * @return {Roo.Element}
27014      */
27015     getEl : function(){
27016         return this.el;  
27017     },
27018     
27019     /**
27020      * Adds a separator
27021      * @return {Roo.Toolbar.Item} The separator item
27022      */
27023     addSeparator : function(){
27024         return this.addItem(new Roo.Toolbar.Separator());
27025     },
27026
27027     /**
27028      * Adds a spacer element
27029      * @return {Roo.Toolbar.Spacer} The spacer item
27030      */
27031     addSpacer : function(){
27032         return this.addItem(new Roo.Toolbar.Spacer());
27033     },
27034
27035     /**
27036      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27037      * @return {Roo.Toolbar.Fill} The fill item
27038      */
27039     addFill : function(){
27040         return this.addItem(new Roo.Toolbar.Fill());
27041     },
27042
27043     /**
27044      * Adds any standard HTML element to the toolbar
27045      * @param {String/HTMLElement/Element} el The element or id of the element to add
27046      * @return {Roo.Toolbar.Item} The element's item
27047      */
27048     addElement : function(el){
27049         return this.addItem(new Roo.Toolbar.Item(el));
27050     },
27051     /**
27052      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27053      * @type Roo.util.MixedCollection  
27054      */
27055     items : false,
27056      
27057     /**
27058      * Adds any Toolbar.Item or subclass
27059      * @param {Roo.Toolbar.Item} item
27060      * @return {Roo.Toolbar.Item} The item
27061      */
27062     addItem : function(item){
27063         var td = this.nextBlock();
27064         item.render(td);
27065         this.items.add(item);
27066         return item;
27067     },
27068     
27069     /**
27070      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27071      * @param {Object/Array} config A button config or array of configs
27072      * @return {Roo.Toolbar.Button/Array}
27073      */
27074     addButton : function(config){
27075         if(config instanceof Array){
27076             var buttons = [];
27077             for(var i = 0, len = config.length; i < len; i++) {
27078                 buttons.push(this.addButton(config[i]));
27079             }
27080             return buttons;
27081         }
27082         var b = config;
27083         if(!(config instanceof Roo.Toolbar.Button)){
27084             b = config.split ?
27085                 new Roo.Toolbar.SplitButton(config) :
27086                 new Roo.Toolbar.Button(config);
27087         }
27088         var td = this.nextBlock();
27089         b.render(td);
27090         this.items.add(b);
27091         return b;
27092     },
27093     
27094     /**
27095      * Adds text to the toolbar
27096      * @param {String} text The text to add
27097      * @return {Roo.Toolbar.Item} The element's item
27098      */
27099     addText : function(text){
27100         return this.addItem(new Roo.Toolbar.TextItem(text));
27101     },
27102     
27103     /**
27104      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27105      * @param {Number} index The index where the item is to be inserted
27106      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27107      * @return {Roo.Toolbar.Button/Item}
27108      */
27109     insertButton : function(index, item){
27110         if(item instanceof Array){
27111             var buttons = [];
27112             for(var i = 0, len = item.length; i < len; i++) {
27113                buttons.push(this.insertButton(index + i, item[i]));
27114             }
27115             return buttons;
27116         }
27117         if (!(item instanceof Roo.Toolbar.Button)){
27118            item = new Roo.Toolbar.Button(item);
27119         }
27120         var td = document.createElement("td");
27121         this.tr.insertBefore(td, this.tr.childNodes[index]);
27122         item.render(td);
27123         this.items.insert(index, item);
27124         return item;
27125     },
27126     
27127     /**
27128      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27129      * @param {Object} config
27130      * @return {Roo.Toolbar.Item} The element's item
27131      */
27132     addDom : function(config, returnEl){
27133         var td = this.nextBlock();
27134         Roo.DomHelper.overwrite(td, config);
27135         var ti = new Roo.Toolbar.Item(td.firstChild);
27136         ti.render(td);
27137         this.items.add(ti);
27138         return ti;
27139     },
27140
27141     /**
27142      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27143      * @type Roo.util.MixedCollection  
27144      */
27145     fields : false,
27146     
27147     /**
27148      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27149      * Note: the field should not have been rendered yet. For a field that has already been
27150      * rendered, use {@link #addElement}.
27151      * @param {Roo.form.Field} field
27152      * @return {Roo.ToolbarItem}
27153      */
27154      
27155       
27156     addField : function(field) {
27157         if (!this.fields) {
27158             var autoId = 0;
27159             this.fields = new Roo.util.MixedCollection(false, function(o){
27160                 return o.id || ("item" + (++autoId));
27161             });
27162
27163         }
27164         
27165         var td = this.nextBlock();
27166         field.render(td);
27167         var ti = new Roo.Toolbar.Item(td.firstChild);
27168         ti.render(td);
27169         this.items.add(ti);
27170         this.fields.add(field);
27171         return ti;
27172     },
27173     /**
27174      * Hide the toolbar
27175      * @method hide
27176      */
27177      
27178       
27179     hide : function()
27180     {
27181         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27182         this.el.child('div').hide();
27183     },
27184     /**
27185      * Show the toolbar
27186      * @method show
27187      */
27188     show : function()
27189     {
27190         this.el.child('div').show();
27191     },
27192       
27193     // private
27194     nextBlock : function(){
27195         var td = document.createElement("td");
27196         this.tr.appendChild(td);
27197         return td;
27198     },
27199
27200     // private
27201     destroy : function(){
27202         if(this.items){ // rendered?
27203             Roo.destroy.apply(Roo, this.items.items);
27204         }
27205         if(this.fields){ // rendered?
27206             Roo.destroy.apply(Roo, this.fields.items);
27207         }
27208         Roo.Element.uncache(this.el, this.tr);
27209     }
27210 };
27211
27212 /**
27213  * @class Roo.Toolbar.Item
27214  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27215  * @constructor
27216  * Creates a new Item
27217  * @param {HTMLElement} el 
27218  */
27219 Roo.Toolbar.Item = function(el){
27220     this.el = Roo.getDom(el);
27221     this.id = Roo.id(this.el);
27222     this.hidden = false;
27223 };
27224
27225 Roo.Toolbar.Item.prototype = {
27226     
27227     /**
27228      * Get this item's HTML Element
27229      * @return {HTMLElement}
27230      */
27231     getEl : function(){
27232        return this.el;  
27233     },
27234
27235     // private
27236     render : function(td){
27237         this.td = td;
27238         td.appendChild(this.el);
27239     },
27240     
27241     /**
27242      * Removes and destroys this item.
27243      */
27244     destroy : function(){
27245         this.td.parentNode.removeChild(this.td);
27246     },
27247     
27248     /**
27249      * Shows this item.
27250      */
27251     show: function(){
27252         this.hidden = false;
27253         this.td.style.display = "";
27254     },
27255     
27256     /**
27257      * Hides this item.
27258      */
27259     hide: function(){
27260         this.hidden = true;
27261         this.td.style.display = "none";
27262     },
27263     
27264     /**
27265      * Convenience function for boolean show/hide.
27266      * @param {Boolean} visible true to show/false to hide
27267      */
27268     setVisible: function(visible){
27269         if(visible) {
27270             this.show();
27271         }else{
27272             this.hide();
27273         }
27274     },
27275     
27276     /**
27277      * Try to focus this item.
27278      */
27279     focus : function(){
27280         Roo.fly(this.el).focus();
27281     },
27282     
27283     /**
27284      * Disables this item.
27285      */
27286     disable : function(){
27287         Roo.fly(this.td).addClass("x-item-disabled");
27288         this.disabled = true;
27289         this.el.disabled = true;
27290     },
27291     
27292     /**
27293      * Enables this item.
27294      */
27295     enable : function(){
27296         Roo.fly(this.td).removeClass("x-item-disabled");
27297         this.disabled = false;
27298         this.el.disabled = false;
27299     }
27300 };
27301
27302
27303 /**
27304  * @class Roo.Toolbar.Separator
27305  * @extends Roo.Toolbar.Item
27306  * A simple toolbar separator class
27307  * @constructor
27308  * Creates a new Separator
27309  */
27310 Roo.Toolbar.Separator = function(){
27311     var s = document.createElement("span");
27312     s.className = "ytb-sep";
27313     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27314 };
27315 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27316     enable:Roo.emptyFn,
27317     disable:Roo.emptyFn,
27318     focus:Roo.emptyFn
27319 });
27320
27321 /**
27322  * @class Roo.Toolbar.Spacer
27323  * @extends Roo.Toolbar.Item
27324  * A simple element that adds extra horizontal space to a toolbar.
27325  * @constructor
27326  * Creates a new Spacer
27327  */
27328 Roo.Toolbar.Spacer = function(){
27329     var s = document.createElement("div");
27330     s.className = "ytb-spacer";
27331     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27332 };
27333 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27334     enable:Roo.emptyFn,
27335     disable:Roo.emptyFn,
27336     focus:Roo.emptyFn
27337 });
27338
27339 /**
27340  * @class Roo.Toolbar.Fill
27341  * @extends Roo.Toolbar.Spacer
27342  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27343  * @constructor
27344  * Creates a new Spacer
27345  */
27346 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27347     // private
27348     render : function(td){
27349         td.style.width = '100%';
27350         Roo.Toolbar.Fill.superclass.render.call(this, td);
27351     }
27352 });
27353
27354 /**
27355  * @class Roo.Toolbar.TextItem
27356  * @extends Roo.Toolbar.Item
27357  * A simple class that renders text directly into a toolbar.
27358  * @constructor
27359  * Creates a new TextItem
27360  * @param {String} text
27361  */
27362 Roo.Toolbar.TextItem = function(text){
27363     if (typeof(text) == 'object') {
27364         text = text.text;
27365     }
27366     var s = document.createElement("span");
27367     s.className = "ytb-text";
27368     s.innerHTML = text;
27369     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27370 };
27371 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27372     enable:Roo.emptyFn,
27373     disable:Roo.emptyFn,
27374     focus:Roo.emptyFn
27375 });
27376
27377 /**
27378  * @class Roo.Toolbar.Button
27379  * @extends Roo.Button
27380  * A button that renders into a toolbar.
27381  * @constructor
27382  * Creates a new Button
27383  * @param {Object} config A standard {@link Roo.Button} config object
27384  */
27385 Roo.Toolbar.Button = function(config){
27386     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27387 };
27388 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27389     render : function(td){
27390         this.td = td;
27391         Roo.Toolbar.Button.superclass.render.call(this, td);
27392     },
27393     
27394     /**
27395      * Removes and destroys this button
27396      */
27397     destroy : function(){
27398         Roo.Toolbar.Button.superclass.destroy.call(this);
27399         this.td.parentNode.removeChild(this.td);
27400     },
27401     
27402     /**
27403      * Shows this button
27404      */
27405     show: function(){
27406         this.hidden = false;
27407         this.td.style.display = "";
27408     },
27409     
27410     /**
27411      * Hides this button
27412      */
27413     hide: function(){
27414         this.hidden = true;
27415         this.td.style.display = "none";
27416     },
27417
27418     /**
27419      * Disables this item
27420      */
27421     disable : function(){
27422         Roo.fly(this.td).addClass("x-item-disabled");
27423         this.disabled = true;
27424     },
27425
27426     /**
27427      * Enables this item
27428      */
27429     enable : function(){
27430         Roo.fly(this.td).removeClass("x-item-disabled");
27431         this.disabled = false;
27432     }
27433 });
27434 // backwards compat
27435 Roo.ToolbarButton = Roo.Toolbar.Button;
27436
27437 /**
27438  * @class Roo.Toolbar.SplitButton
27439  * @extends Roo.SplitButton
27440  * A menu button that renders into a toolbar.
27441  * @constructor
27442  * Creates a new SplitButton
27443  * @param {Object} config A standard {@link Roo.SplitButton} config object
27444  */
27445 Roo.Toolbar.SplitButton = function(config){
27446     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27447 };
27448 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27449     render : function(td){
27450         this.td = td;
27451         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27452     },
27453     
27454     /**
27455      * Removes and destroys this button
27456      */
27457     destroy : function(){
27458         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27459         this.td.parentNode.removeChild(this.td);
27460     },
27461     
27462     /**
27463      * Shows this button
27464      */
27465     show: function(){
27466         this.hidden = false;
27467         this.td.style.display = "";
27468     },
27469     
27470     /**
27471      * Hides this button
27472      */
27473     hide: function(){
27474         this.hidden = true;
27475         this.td.style.display = "none";
27476     }
27477 });
27478
27479 // backwards compat
27480 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27481  * Based on:
27482  * Ext JS Library 1.1.1
27483  * Copyright(c) 2006-2007, Ext JS, LLC.
27484  *
27485  * Originally Released Under LGPL - original licence link has changed is not relivant.
27486  *
27487  * Fork - LGPL
27488  * <script type="text/javascript">
27489  */
27490  
27491 /**
27492  * @class Roo.PagingToolbar
27493  * @extends Roo.Toolbar
27494  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27495  * @constructor
27496  * Create a new PagingToolbar
27497  * @param {Object} config The config object
27498  */
27499 Roo.PagingToolbar = function(el, ds, config)
27500 {
27501     // old args format still supported... - xtype is prefered..
27502     if (typeof(el) == 'object' && el.xtype) {
27503         // created from xtype...
27504         config = el;
27505         ds = el.dataSource;
27506         el = config.container;
27507     }
27508     var items = [];
27509     if (config.items) {
27510         items = config.items;
27511         config.items = [];
27512     }
27513     
27514     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27515     this.ds = ds;
27516     this.cursor = 0;
27517     this.renderButtons(this.el);
27518     this.bind(ds);
27519     
27520     // supprot items array.
27521    
27522     Roo.each(items, function(e) {
27523         this.add(Roo.factory(e));
27524     },this);
27525     
27526 };
27527
27528 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27529     /**
27530      * @cfg {Roo.data.Store} dataSource
27531      * The underlying data store providing the paged data
27532      */
27533     /**
27534      * @cfg {String/HTMLElement/Element} container
27535      * container The id or element that will contain the toolbar
27536      */
27537     /**
27538      * @cfg {Boolean} displayInfo
27539      * True to display the displayMsg (defaults to false)
27540      */
27541     /**
27542      * @cfg {Number} pageSize
27543      * The number of records to display per page (defaults to 20)
27544      */
27545     pageSize: 20,
27546     /**
27547      * @cfg {String} displayMsg
27548      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27549      */
27550     displayMsg : 'Displaying {0} - {1} of {2}',
27551     /**
27552      * @cfg {String} emptyMsg
27553      * The message to display when no records are found (defaults to "No data to display")
27554      */
27555     emptyMsg : 'No data to display',
27556     /**
27557      * Customizable piece of the default paging text (defaults to "Page")
27558      * @type String
27559      */
27560     beforePageText : "Page",
27561     /**
27562      * Customizable piece of the default paging text (defaults to "of %0")
27563      * @type String
27564      */
27565     afterPageText : "of {0}",
27566     /**
27567      * Customizable piece of the default paging text (defaults to "First Page")
27568      * @type String
27569      */
27570     firstText : "First Page",
27571     /**
27572      * Customizable piece of the default paging text (defaults to "Previous Page")
27573      * @type String
27574      */
27575     prevText : "Previous Page",
27576     /**
27577      * Customizable piece of the default paging text (defaults to "Next Page")
27578      * @type String
27579      */
27580     nextText : "Next Page",
27581     /**
27582      * Customizable piece of the default paging text (defaults to "Last Page")
27583      * @type String
27584      */
27585     lastText : "Last Page",
27586     /**
27587      * Customizable piece of the default paging text (defaults to "Refresh")
27588      * @type String
27589      */
27590     refreshText : "Refresh",
27591
27592     // private
27593     renderButtons : function(el){
27594         Roo.PagingToolbar.superclass.render.call(this, el);
27595         this.first = this.addButton({
27596             tooltip: this.firstText,
27597             cls: "x-btn-icon x-grid-page-first",
27598             disabled: true,
27599             handler: this.onClick.createDelegate(this, ["first"])
27600         });
27601         this.prev = this.addButton({
27602             tooltip: this.prevText,
27603             cls: "x-btn-icon x-grid-page-prev",
27604             disabled: true,
27605             handler: this.onClick.createDelegate(this, ["prev"])
27606         });
27607         //this.addSeparator();
27608         this.add(this.beforePageText);
27609         this.field = Roo.get(this.addDom({
27610            tag: "input",
27611            type: "text",
27612            size: "3",
27613            value: "1",
27614            cls: "x-grid-page-number"
27615         }).el);
27616         this.field.on("keydown", this.onPagingKeydown, this);
27617         this.field.on("focus", function(){this.dom.select();});
27618         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27619         this.field.setHeight(18);
27620         //this.addSeparator();
27621         this.next = this.addButton({
27622             tooltip: this.nextText,
27623             cls: "x-btn-icon x-grid-page-next",
27624             disabled: true,
27625             handler: this.onClick.createDelegate(this, ["next"])
27626         });
27627         this.last = this.addButton({
27628             tooltip: this.lastText,
27629             cls: "x-btn-icon x-grid-page-last",
27630             disabled: true,
27631             handler: this.onClick.createDelegate(this, ["last"])
27632         });
27633         //this.addSeparator();
27634         this.loading = this.addButton({
27635             tooltip: this.refreshText,
27636             cls: "x-btn-icon x-grid-loading",
27637             handler: this.onClick.createDelegate(this, ["refresh"])
27638         });
27639
27640         if(this.displayInfo){
27641             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27642         }
27643     },
27644
27645     // private
27646     updateInfo : function(){
27647         if(this.displayEl){
27648             var count = this.ds.getCount();
27649             var msg = count == 0 ?
27650                 this.emptyMsg :
27651                 String.format(
27652                     this.displayMsg,
27653                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27654                 );
27655             this.displayEl.update(msg);
27656         }
27657     },
27658
27659     // private
27660     onLoad : function(ds, r, o){
27661        this.cursor = o.params ? o.params.start : 0;
27662        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27663
27664        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27665        this.field.dom.value = ap;
27666        this.first.setDisabled(ap == 1);
27667        this.prev.setDisabled(ap == 1);
27668        this.next.setDisabled(ap == ps);
27669        this.last.setDisabled(ap == ps);
27670        this.loading.enable();
27671        this.updateInfo();
27672     },
27673
27674     // private
27675     getPageData : function(){
27676         var total = this.ds.getTotalCount();
27677         return {
27678             total : total,
27679             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27680             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27681         };
27682     },
27683
27684     // private
27685     onLoadError : function(){
27686         this.loading.enable();
27687     },
27688
27689     // private
27690     onPagingKeydown : function(e){
27691         var k = e.getKey();
27692         var d = this.getPageData();
27693         if(k == e.RETURN){
27694             var v = this.field.dom.value, pageNum;
27695             if(!v || isNaN(pageNum = parseInt(v, 10))){
27696                 this.field.dom.value = d.activePage;
27697                 return;
27698             }
27699             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27700             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27701             e.stopEvent();
27702         }
27703         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))
27704         {
27705           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27706           this.field.dom.value = pageNum;
27707           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27708           e.stopEvent();
27709         }
27710         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27711         {
27712           var v = this.field.dom.value, pageNum; 
27713           var increment = (e.shiftKey) ? 10 : 1;
27714           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27715             increment *= -1;
27716           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27717             this.field.dom.value = d.activePage;
27718             return;
27719           }
27720           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27721           {
27722             this.field.dom.value = parseInt(v, 10) + increment;
27723             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27724             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27725           }
27726           e.stopEvent();
27727         }
27728     },
27729
27730     // private
27731     beforeLoad : function(){
27732         if(this.loading){
27733             this.loading.disable();
27734         }
27735     },
27736
27737     // private
27738     onClick : function(which){
27739         var ds = this.ds;
27740         switch(which){
27741             case "first":
27742                 ds.load({params:{start: 0, limit: this.pageSize}});
27743             break;
27744             case "prev":
27745                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27746             break;
27747             case "next":
27748                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27749             break;
27750             case "last":
27751                 var total = ds.getTotalCount();
27752                 var extra = total % this.pageSize;
27753                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27754                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27755             break;
27756             case "refresh":
27757                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27758             break;
27759         }
27760     },
27761
27762     /**
27763      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27764      * @param {Roo.data.Store} store The data store to unbind
27765      */
27766     unbind : function(ds){
27767         ds.un("beforeload", this.beforeLoad, this);
27768         ds.un("load", this.onLoad, this);
27769         ds.un("loadexception", this.onLoadError, this);
27770         ds.un("remove", this.updateInfo, this);
27771         ds.un("add", this.updateInfo, this);
27772         this.ds = undefined;
27773     },
27774
27775     /**
27776      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27777      * @param {Roo.data.Store} store The data store to bind
27778      */
27779     bind : function(ds){
27780         ds.on("beforeload", this.beforeLoad, this);
27781         ds.on("load", this.onLoad, this);
27782         ds.on("loadexception", this.onLoadError, this);
27783         ds.on("remove", this.updateInfo, this);
27784         ds.on("add", this.updateInfo, this);
27785         this.ds = ds;
27786     }
27787 });/*
27788  * Based on:
27789  * Ext JS Library 1.1.1
27790  * Copyright(c) 2006-2007, Ext JS, LLC.
27791  *
27792  * Originally Released Under LGPL - original licence link has changed is not relivant.
27793  *
27794  * Fork - LGPL
27795  * <script type="text/javascript">
27796  */
27797
27798 /**
27799  * @class Roo.Resizable
27800  * @extends Roo.util.Observable
27801  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27802  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27803  * 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
27804  * the element will be wrapped for you automatically.</p>
27805  * <p>Here is the list of valid resize handles:</p>
27806  * <pre>
27807 Value   Description
27808 ------  -------------------
27809  'n'     north
27810  's'     south
27811  'e'     east
27812  'w'     west
27813  'nw'    northwest
27814  'sw'    southwest
27815  'se'    southeast
27816  'ne'    northeast
27817  'hd'    horizontal drag
27818  'all'   all
27819 </pre>
27820  * <p>Here's an example showing the creation of a typical Resizable:</p>
27821  * <pre><code>
27822 var resizer = new Roo.Resizable("element-id", {
27823     handles: 'all',
27824     minWidth: 200,
27825     minHeight: 100,
27826     maxWidth: 500,
27827     maxHeight: 400,
27828     pinned: true
27829 });
27830 resizer.on("resize", myHandler);
27831 </code></pre>
27832  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27833  * resizer.east.setDisplayed(false);</p>
27834  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27835  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27836  * resize operation's new size (defaults to [0, 0])
27837  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27838  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27839  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27840  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27841  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27842  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27843  * @cfg {Number} width The width of the element in pixels (defaults to null)
27844  * @cfg {Number} height The height of the element in pixels (defaults to null)
27845  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27846  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27847  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27848  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27849  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27850  * in favor of the handles config option (defaults to false)
27851  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27852  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27853  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27854  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27855  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27856  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27857  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27858  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27859  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27860  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27861  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27862  * @constructor
27863  * Create a new resizable component
27864  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27865  * @param {Object} config configuration options
27866   */
27867 Roo.Resizable = function(el, config)
27868 {
27869     this.el = Roo.get(el);
27870
27871     if(config && config.wrap){
27872         config.resizeChild = this.el;
27873         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27874         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27875         this.el.setStyle("overflow", "hidden");
27876         this.el.setPositioning(config.resizeChild.getPositioning());
27877         config.resizeChild.clearPositioning();
27878         if(!config.width || !config.height){
27879             var csize = config.resizeChild.getSize();
27880             this.el.setSize(csize.width, csize.height);
27881         }
27882         if(config.pinned && !config.adjustments){
27883             config.adjustments = "auto";
27884         }
27885     }
27886
27887     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27888     this.proxy.unselectable();
27889     this.proxy.enableDisplayMode('block');
27890
27891     Roo.apply(this, config);
27892
27893     if(this.pinned){
27894         this.disableTrackOver = true;
27895         this.el.addClass("x-resizable-pinned");
27896     }
27897     // if the element isn't positioned, make it relative
27898     var position = this.el.getStyle("position");
27899     if(position != "absolute" && position != "fixed"){
27900         this.el.setStyle("position", "relative");
27901     }
27902     if(!this.handles){ // no handles passed, must be legacy style
27903         this.handles = 's,e,se';
27904         if(this.multiDirectional){
27905             this.handles += ',n,w';
27906         }
27907     }
27908     if(this.handles == "all"){
27909         this.handles = "n s e w ne nw se sw";
27910     }
27911     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27912     var ps = Roo.Resizable.positions;
27913     for(var i = 0, len = hs.length; i < len; i++){
27914         if(hs[i] && ps[hs[i]]){
27915             var pos = ps[hs[i]];
27916             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27917         }
27918     }
27919     // legacy
27920     this.corner = this.southeast;
27921     
27922     // updateBox = the box can move..
27923     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27924         this.updateBox = true;
27925     }
27926
27927     this.activeHandle = null;
27928
27929     if(this.resizeChild){
27930         if(typeof this.resizeChild == "boolean"){
27931             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27932         }else{
27933             this.resizeChild = Roo.get(this.resizeChild, true);
27934         }
27935     }
27936     
27937     if(this.adjustments == "auto"){
27938         var rc = this.resizeChild;
27939         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27940         if(rc && (hw || hn)){
27941             rc.position("relative");
27942             rc.setLeft(hw ? hw.el.getWidth() : 0);
27943             rc.setTop(hn ? hn.el.getHeight() : 0);
27944         }
27945         this.adjustments = [
27946             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27947             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27948         ];
27949     }
27950
27951     if(this.draggable){
27952         this.dd = this.dynamic ?
27953             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27954         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27955     }
27956
27957     // public events
27958     this.addEvents({
27959         /**
27960          * @event beforeresize
27961          * Fired before resize is allowed. Set enabled to false to cancel resize.
27962          * @param {Roo.Resizable} this
27963          * @param {Roo.EventObject} e The mousedown event
27964          */
27965         "beforeresize" : true,
27966         /**
27967          * @event resize
27968          * Fired after a resize.
27969          * @param {Roo.Resizable} this
27970          * @param {Number} width The new width
27971          * @param {Number} height The new height
27972          * @param {Roo.EventObject} e The mouseup event
27973          */
27974         "resize" : true
27975     });
27976
27977     if(this.width !== null && this.height !== null){
27978         this.resizeTo(this.width, this.height);
27979     }else{
27980         this.updateChildSize();
27981     }
27982     if(Roo.isIE){
27983         this.el.dom.style.zoom = 1;
27984     }
27985     Roo.Resizable.superclass.constructor.call(this);
27986 };
27987
27988 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27989         resizeChild : false,
27990         adjustments : [0, 0],
27991         minWidth : 5,
27992         minHeight : 5,
27993         maxWidth : 10000,
27994         maxHeight : 10000,
27995         enabled : true,
27996         animate : false,
27997         duration : .35,
27998         dynamic : false,
27999         handles : false,
28000         multiDirectional : false,
28001         disableTrackOver : false,
28002         easing : 'easeOutStrong',
28003         widthIncrement : 0,
28004         heightIncrement : 0,
28005         pinned : false,
28006         width : null,
28007         height : null,
28008         preserveRatio : false,
28009         transparent: false,
28010         minX: 0,
28011         minY: 0,
28012         draggable: false,
28013
28014         /**
28015          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28016          */
28017         constrainTo: undefined,
28018         /**
28019          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28020          */
28021         resizeRegion: undefined,
28022
28023
28024     /**
28025      * Perform a manual resize
28026      * @param {Number} width
28027      * @param {Number} height
28028      */
28029     resizeTo : function(width, height){
28030         this.el.setSize(width, height);
28031         this.updateChildSize();
28032         this.fireEvent("resize", this, width, height, null);
28033     },
28034
28035     // private
28036     startSizing : function(e, handle){
28037         this.fireEvent("beforeresize", this, e);
28038         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28039
28040             if(!this.overlay){
28041                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28042                 this.overlay.unselectable();
28043                 this.overlay.enableDisplayMode("block");
28044                 this.overlay.on("mousemove", this.onMouseMove, this);
28045                 this.overlay.on("mouseup", this.onMouseUp, this);
28046             }
28047             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28048
28049             this.resizing = true;
28050             this.startBox = this.el.getBox();
28051             this.startPoint = e.getXY();
28052             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28053                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28054
28055             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28056             this.overlay.show();
28057
28058             if(this.constrainTo) {
28059                 var ct = Roo.get(this.constrainTo);
28060                 this.resizeRegion = ct.getRegion().adjust(
28061                     ct.getFrameWidth('t'),
28062                     ct.getFrameWidth('l'),
28063                     -ct.getFrameWidth('b'),
28064                     -ct.getFrameWidth('r')
28065                 );
28066             }
28067
28068             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28069             this.proxy.show();
28070             this.proxy.setBox(this.startBox);
28071             if(!this.dynamic){
28072                 this.proxy.setStyle('visibility', 'visible');
28073             }
28074         }
28075     },
28076
28077     // private
28078     onMouseDown : function(handle, e){
28079         if(this.enabled){
28080             e.stopEvent();
28081             this.activeHandle = handle;
28082             this.startSizing(e, handle);
28083         }
28084     },
28085
28086     // private
28087     onMouseUp : function(e){
28088         var size = this.resizeElement();
28089         this.resizing = false;
28090         this.handleOut();
28091         this.overlay.hide();
28092         this.proxy.hide();
28093         this.fireEvent("resize", this, size.width, size.height, e);
28094     },
28095
28096     // private
28097     updateChildSize : function(){
28098         if(this.resizeChild){
28099             var el = this.el;
28100             var child = this.resizeChild;
28101             var adj = this.adjustments;
28102             if(el.dom.offsetWidth){
28103                 var b = el.getSize(true);
28104                 child.setSize(b.width+adj[0], b.height+adj[1]);
28105             }
28106             // Second call here for IE
28107             // The first call enables instant resizing and
28108             // the second call corrects scroll bars if they
28109             // exist
28110             if(Roo.isIE){
28111                 setTimeout(function(){
28112                     if(el.dom.offsetWidth){
28113                         var b = el.getSize(true);
28114                         child.setSize(b.width+adj[0], b.height+adj[1]);
28115                     }
28116                 }, 10);
28117             }
28118         }
28119     },
28120
28121     // private
28122     snap : function(value, inc, min){
28123         if(!inc || !value) return value;
28124         var newValue = value;
28125         var m = value % inc;
28126         if(m > 0){
28127             if(m > (inc/2)){
28128                 newValue = value + (inc-m);
28129             }else{
28130                 newValue = value - m;
28131             }
28132         }
28133         return Math.max(min, newValue);
28134     },
28135
28136     // private
28137     resizeElement : function(){
28138         var box = this.proxy.getBox();
28139         if(this.updateBox){
28140             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28141         }else{
28142             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28143         }
28144         this.updateChildSize();
28145         if(!this.dynamic){
28146             this.proxy.hide();
28147         }
28148         return box;
28149     },
28150
28151     // private
28152     constrain : function(v, diff, m, mx){
28153         if(v - diff < m){
28154             diff = v - m;
28155         }else if(v - diff > mx){
28156             diff = mx - v;
28157         }
28158         return diff;
28159     },
28160
28161     // private
28162     onMouseMove : function(e){
28163         if(this.enabled){
28164             try{// try catch so if something goes wrong the user doesn't get hung
28165
28166             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28167                 return;
28168             }
28169
28170             //var curXY = this.startPoint;
28171             var curSize = this.curSize || this.startBox;
28172             var x = this.startBox.x, y = this.startBox.y;
28173             var ox = x, oy = y;
28174             var w = curSize.width, h = curSize.height;
28175             var ow = w, oh = h;
28176             var mw = this.minWidth, mh = this.minHeight;
28177             var mxw = this.maxWidth, mxh = this.maxHeight;
28178             var wi = this.widthIncrement;
28179             var hi = this.heightIncrement;
28180
28181             var eventXY = e.getXY();
28182             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28183             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28184
28185             var pos = this.activeHandle.position;
28186
28187             switch(pos){
28188                 case "east":
28189                     w += diffX;
28190                     w = Math.min(Math.max(mw, w), mxw);
28191                     break;
28192              
28193                 case "south":
28194                     h += diffY;
28195                     h = Math.min(Math.max(mh, h), mxh);
28196                     break;
28197                 case "southeast":
28198                     w += diffX;
28199                     h += diffY;
28200                     w = Math.min(Math.max(mw, w), mxw);
28201                     h = Math.min(Math.max(mh, h), mxh);
28202                     break;
28203                 case "north":
28204                     diffY = this.constrain(h, diffY, mh, mxh);
28205                     y += diffY;
28206                     h -= diffY;
28207                     break;
28208                 case "hdrag":
28209                     
28210                     if (wi) {
28211                         var adiffX = Math.abs(diffX);
28212                         var sub = (adiffX % wi); // how much 
28213                         if (sub > (wi/2)) { // far enough to snap
28214                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28215                         } else {
28216                             // remove difference.. 
28217                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28218                         }
28219                     }
28220                     x += diffX;
28221                     x = Math.max(this.minX, x);
28222                     break;
28223                 case "west":
28224                     diffX = this.constrain(w, diffX, mw, mxw);
28225                     x += diffX;
28226                     w -= diffX;
28227                     break;
28228                 case "northeast":
28229                     w += diffX;
28230                     w = Math.min(Math.max(mw, w), mxw);
28231                     diffY = this.constrain(h, diffY, mh, mxh);
28232                     y += diffY;
28233                     h -= diffY;
28234                     break;
28235                 case "northwest":
28236                     diffX = this.constrain(w, diffX, mw, mxw);
28237                     diffY = this.constrain(h, diffY, mh, mxh);
28238                     y += diffY;
28239                     h -= diffY;
28240                     x += diffX;
28241                     w -= diffX;
28242                     break;
28243                case "southwest":
28244                     diffX = this.constrain(w, diffX, mw, mxw);
28245                     h += diffY;
28246                     h = Math.min(Math.max(mh, h), mxh);
28247                     x += diffX;
28248                     w -= diffX;
28249                     break;
28250             }
28251
28252             var sw = this.snap(w, wi, mw);
28253             var sh = this.snap(h, hi, mh);
28254             if(sw != w || sh != h){
28255                 switch(pos){
28256                     case "northeast":
28257                         y -= sh - h;
28258                     break;
28259                     case "north":
28260                         y -= sh - h;
28261                         break;
28262                     case "southwest":
28263                         x -= sw - w;
28264                     break;
28265                     case "west":
28266                         x -= sw - w;
28267                         break;
28268                     case "northwest":
28269                         x -= sw - w;
28270                         y -= sh - h;
28271                     break;
28272                 }
28273                 w = sw;
28274                 h = sh;
28275             }
28276
28277             if(this.preserveRatio){
28278                 switch(pos){
28279                     case "southeast":
28280                     case "east":
28281                         h = oh * (w/ow);
28282                         h = Math.min(Math.max(mh, h), mxh);
28283                         w = ow * (h/oh);
28284                        break;
28285                     case "south":
28286                         w = ow * (h/oh);
28287                         w = Math.min(Math.max(mw, w), mxw);
28288                         h = oh * (w/ow);
28289                         break;
28290                     case "northeast":
28291                         w = ow * (h/oh);
28292                         w = Math.min(Math.max(mw, w), mxw);
28293                         h = oh * (w/ow);
28294                     break;
28295                     case "north":
28296                         var tw = w;
28297                         w = ow * (h/oh);
28298                         w = Math.min(Math.max(mw, w), mxw);
28299                         h = oh * (w/ow);
28300                         x += (tw - w) / 2;
28301                         break;
28302                     case "southwest":
28303                         h = oh * (w/ow);
28304                         h = Math.min(Math.max(mh, h), mxh);
28305                         var tw = w;
28306                         w = ow * (h/oh);
28307                         x += tw - w;
28308                         break;
28309                     case "west":
28310                         var th = h;
28311                         h = oh * (w/ow);
28312                         h = Math.min(Math.max(mh, h), mxh);
28313                         y += (th - h) / 2;
28314                         var tw = w;
28315                         w = ow * (h/oh);
28316                         x += tw - w;
28317                        break;
28318                     case "northwest":
28319                         var tw = w;
28320                         var th = h;
28321                         h = oh * (w/ow);
28322                         h = Math.min(Math.max(mh, h), mxh);
28323                         w = ow * (h/oh);
28324                         y += th - h;
28325                         x += tw - w;
28326                        break;
28327
28328                 }
28329             }
28330             if (pos == 'hdrag') {
28331                 w = ow;
28332             }
28333             this.proxy.setBounds(x, y, w, h);
28334             if(this.dynamic){
28335                 this.resizeElement();
28336             }
28337             }catch(e){}
28338         }
28339     },
28340
28341     // private
28342     handleOver : function(){
28343         if(this.enabled){
28344             this.el.addClass("x-resizable-over");
28345         }
28346     },
28347
28348     // private
28349     handleOut : function(){
28350         if(!this.resizing){
28351             this.el.removeClass("x-resizable-over");
28352         }
28353     },
28354
28355     /**
28356      * Returns the element this component is bound to.
28357      * @return {Roo.Element}
28358      */
28359     getEl : function(){
28360         return this.el;
28361     },
28362
28363     /**
28364      * Returns the resizeChild element (or null).
28365      * @return {Roo.Element}
28366      */
28367     getResizeChild : function(){
28368         return this.resizeChild;
28369     },
28370
28371     /**
28372      * Destroys this resizable. If the element was wrapped and
28373      * removeEl is not true then the element remains.
28374      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28375      */
28376     destroy : function(removeEl){
28377         this.proxy.remove();
28378         if(this.overlay){
28379             this.overlay.removeAllListeners();
28380             this.overlay.remove();
28381         }
28382         var ps = Roo.Resizable.positions;
28383         for(var k in ps){
28384             if(typeof ps[k] != "function" && this[ps[k]]){
28385                 var h = this[ps[k]];
28386                 h.el.removeAllListeners();
28387                 h.el.remove();
28388             }
28389         }
28390         if(removeEl){
28391             this.el.update("");
28392             this.el.remove();
28393         }
28394     }
28395 });
28396
28397 // private
28398 // hash to map config positions to true positions
28399 Roo.Resizable.positions = {
28400     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28401     hd: "hdrag"
28402 };
28403
28404 // private
28405 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28406     if(!this.tpl){
28407         // only initialize the template if resizable is used
28408         var tpl = Roo.DomHelper.createTemplate(
28409             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28410         );
28411         tpl.compile();
28412         Roo.Resizable.Handle.prototype.tpl = tpl;
28413     }
28414     this.position = pos;
28415     this.rz = rz;
28416     // show north drag fro topdra
28417     var handlepos = pos == 'hdrag' ? 'north' : pos;
28418     
28419     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28420     if (pos == 'hdrag') {
28421         this.el.setStyle('cursor', 'pointer');
28422     }
28423     this.el.unselectable();
28424     if(transparent){
28425         this.el.setOpacity(0);
28426     }
28427     this.el.on("mousedown", this.onMouseDown, this);
28428     if(!disableTrackOver){
28429         this.el.on("mouseover", this.onMouseOver, this);
28430         this.el.on("mouseout", this.onMouseOut, this);
28431     }
28432 };
28433
28434 // private
28435 Roo.Resizable.Handle.prototype = {
28436     afterResize : function(rz){
28437         // do nothing
28438     },
28439     // private
28440     onMouseDown : function(e){
28441         this.rz.onMouseDown(this, e);
28442     },
28443     // private
28444     onMouseOver : function(e){
28445         this.rz.handleOver(this, e);
28446     },
28447     // private
28448     onMouseOut : function(e){
28449         this.rz.handleOut(this, e);
28450     }
28451 };/*
28452  * Based on:
28453  * Ext JS Library 1.1.1
28454  * Copyright(c) 2006-2007, Ext JS, LLC.
28455  *
28456  * Originally Released Under LGPL - original licence link has changed is not relivant.
28457  *
28458  * Fork - LGPL
28459  * <script type="text/javascript">
28460  */
28461
28462 /**
28463  * @class Roo.Editor
28464  * @extends Roo.Component
28465  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28466  * @constructor
28467  * Create a new Editor
28468  * @param {Roo.form.Field} field The Field object (or descendant)
28469  * @param {Object} config The config object
28470  */
28471 Roo.Editor = function(field, config){
28472     Roo.Editor.superclass.constructor.call(this, config);
28473     this.field = field;
28474     this.addEvents({
28475         /**
28476              * @event beforestartedit
28477              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28478              * false from the handler of this event.
28479              * @param {Editor} this
28480              * @param {Roo.Element} boundEl The underlying element bound to this editor
28481              * @param {Mixed} value The field value being set
28482              */
28483         "beforestartedit" : true,
28484         /**
28485              * @event startedit
28486              * Fires when this editor is displayed
28487              * @param {Roo.Element} boundEl The underlying element bound to this editor
28488              * @param {Mixed} value The starting field value
28489              */
28490         "startedit" : true,
28491         /**
28492              * @event beforecomplete
28493              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28494              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28495              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28496              * event will not fire since no edit actually occurred.
28497              * @param {Editor} this
28498              * @param {Mixed} value The current field value
28499              * @param {Mixed} startValue The original field value
28500              */
28501         "beforecomplete" : true,
28502         /**
28503              * @event complete
28504              * Fires after editing is complete and any changed value has been written to the underlying field.
28505              * @param {Editor} this
28506              * @param {Mixed} value The current field value
28507              * @param {Mixed} startValue The original field value
28508              */
28509         "complete" : true,
28510         /**
28511          * @event specialkey
28512          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28513          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28514          * @param {Roo.form.Field} this
28515          * @param {Roo.EventObject} e The event object
28516          */
28517         "specialkey" : true
28518     });
28519 };
28520
28521 Roo.extend(Roo.Editor, Roo.Component, {
28522     /**
28523      * @cfg {Boolean/String} autosize
28524      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28525      * or "height" to adopt the height only (defaults to false)
28526      */
28527     /**
28528      * @cfg {Boolean} revertInvalid
28529      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28530      * validation fails (defaults to true)
28531      */
28532     /**
28533      * @cfg {Boolean} ignoreNoChange
28534      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28535      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28536      * will never be ignored.
28537      */
28538     /**
28539      * @cfg {Boolean} hideEl
28540      * False to keep the bound element visible while the editor is displayed (defaults to true)
28541      */
28542     /**
28543      * @cfg {Mixed} value
28544      * The data value of the underlying field (defaults to "")
28545      */
28546     value : "",
28547     /**
28548      * @cfg {String} alignment
28549      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28550      */
28551     alignment: "c-c?",
28552     /**
28553      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28554      * for bottom-right shadow (defaults to "frame")
28555      */
28556     shadow : "frame",
28557     /**
28558      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28559      */
28560     constrain : false,
28561     /**
28562      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28563      */
28564     completeOnEnter : false,
28565     /**
28566      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28567      */
28568     cancelOnEsc : false,
28569     /**
28570      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28571      */
28572     updateEl : false,
28573
28574     // private
28575     onRender : function(ct, position){
28576         this.el = new Roo.Layer({
28577             shadow: this.shadow,
28578             cls: "x-editor",
28579             parentEl : ct,
28580             shim : this.shim,
28581             shadowOffset:4,
28582             id: this.id,
28583             constrain: this.constrain
28584         });
28585         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28586         if(this.field.msgTarget != 'title'){
28587             this.field.msgTarget = 'qtip';
28588         }
28589         this.field.render(this.el);
28590         if(Roo.isGecko){
28591             this.field.el.dom.setAttribute('autocomplete', 'off');
28592         }
28593         this.field.on("specialkey", this.onSpecialKey, this);
28594         if(this.swallowKeys){
28595             this.field.el.swallowEvent(['keydown','keypress']);
28596         }
28597         this.field.show();
28598         this.field.on("blur", this.onBlur, this);
28599         if(this.field.grow){
28600             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28601         }
28602     },
28603
28604     onSpecialKey : function(field, e)
28605     {
28606         //Roo.log('editor onSpecialKey');
28607         if(this.completeOnEnter && e.getKey() == e.ENTER){
28608             e.stopEvent();
28609             this.completeEdit();
28610             return;
28611         }
28612         // do not fire special key otherwise it might hide close the editor...
28613         if(e.getKey() == e.ENTER){    
28614             return;
28615         }
28616         if(this.cancelOnEsc && e.getKey() == e.ESC){
28617             this.cancelEdit();
28618             return;
28619         } 
28620         this.fireEvent('specialkey', field, e);
28621     
28622     },
28623
28624     /**
28625      * Starts the editing process and shows the editor.
28626      * @param {String/HTMLElement/Element} el The element to edit
28627      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28628       * to the innerHTML of el.
28629      */
28630     startEdit : function(el, value){
28631         if(this.editing){
28632             this.completeEdit();
28633         }
28634         this.boundEl = Roo.get(el);
28635         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28636         if(!this.rendered){
28637             this.render(this.parentEl || document.body);
28638         }
28639         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28640             return;
28641         }
28642         this.startValue = v;
28643         this.field.setValue(v);
28644         if(this.autoSize){
28645             var sz = this.boundEl.getSize();
28646             switch(this.autoSize){
28647                 case "width":
28648                 this.setSize(sz.width,  "");
28649                 break;
28650                 case "height":
28651                 this.setSize("",  sz.height);
28652                 break;
28653                 default:
28654                 this.setSize(sz.width,  sz.height);
28655             }
28656         }
28657         this.el.alignTo(this.boundEl, this.alignment);
28658         this.editing = true;
28659         if(Roo.QuickTips){
28660             Roo.QuickTips.disable();
28661         }
28662         this.show();
28663     },
28664
28665     /**
28666      * Sets the height and width of this editor.
28667      * @param {Number} width The new width
28668      * @param {Number} height The new height
28669      */
28670     setSize : function(w, h){
28671         this.field.setSize(w, h);
28672         if(this.el){
28673             this.el.sync();
28674         }
28675     },
28676
28677     /**
28678      * Realigns the editor to the bound field based on the current alignment config value.
28679      */
28680     realign : function(){
28681         this.el.alignTo(this.boundEl, this.alignment);
28682     },
28683
28684     /**
28685      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28686      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28687      */
28688     completeEdit : function(remainVisible){
28689         if(!this.editing){
28690             return;
28691         }
28692         var v = this.getValue();
28693         if(this.revertInvalid !== false && !this.field.isValid()){
28694             v = this.startValue;
28695             this.cancelEdit(true);
28696         }
28697         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28698             this.editing = false;
28699             this.hide();
28700             return;
28701         }
28702         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28703             this.editing = false;
28704             if(this.updateEl && this.boundEl){
28705                 this.boundEl.update(v);
28706             }
28707             if(remainVisible !== true){
28708                 this.hide();
28709             }
28710             this.fireEvent("complete", this, v, this.startValue);
28711         }
28712     },
28713
28714     // private
28715     onShow : function(){
28716         this.el.show();
28717         if(this.hideEl !== false){
28718             this.boundEl.hide();
28719         }
28720         this.field.show();
28721         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28722             this.fixIEFocus = true;
28723             this.deferredFocus.defer(50, this);
28724         }else{
28725             this.field.focus();
28726         }
28727         this.fireEvent("startedit", this.boundEl, this.startValue);
28728     },
28729
28730     deferredFocus : function(){
28731         if(this.editing){
28732             this.field.focus();
28733         }
28734     },
28735
28736     /**
28737      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28738      * reverted to the original starting value.
28739      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28740      * cancel (defaults to false)
28741      */
28742     cancelEdit : function(remainVisible){
28743         if(this.editing){
28744             this.setValue(this.startValue);
28745             if(remainVisible !== true){
28746                 this.hide();
28747             }
28748         }
28749     },
28750
28751     // private
28752     onBlur : function(){
28753         if(this.allowBlur !== true && this.editing){
28754             this.completeEdit();
28755         }
28756     },
28757
28758     // private
28759     onHide : function(){
28760         if(this.editing){
28761             this.completeEdit();
28762             return;
28763         }
28764         this.field.blur();
28765         if(this.field.collapse){
28766             this.field.collapse();
28767         }
28768         this.el.hide();
28769         if(this.hideEl !== false){
28770             this.boundEl.show();
28771         }
28772         if(Roo.QuickTips){
28773             Roo.QuickTips.enable();
28774         }
28775     },
28776
28777     /**
28778      * Sets the data value of the editor
28779      * @param {Mixed} value Any valid value supported by the underlying field
28780      */
28781     setValue : function(v){
28782         this.field.setValue(v);
28783     },
28784
28785     /**
28786      * Gets the data value of the editor
28787      * @return {Mixed} The data value
28788      */
28789     getValue : function(){
28790         return this.field.getValue();
28791     }
28792 });/*
28793  * Based on:
28794  * Ext JS Library 1.1.1
28795  * Copyright(c) 2006-2007, Ext JS, LLC.
28796  *
28797  * Originally Released Under LGPL - original licence link has changed is not relivant.
28798  *
28799  * Fork - LGPL
28800  * <script type="text/javascript">
28801  */
28802  
28803 /**
28804  * @class Roo.BasicDialog
28805  * @extends Roo.util.Observable
28806  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28807  * <pre><code>
28808 var dlg = new Roo.BasicDialog("my-dlg", {
28809     height: 200,
28810     width: 300,
28811     minHeight: 100,
28812     minWidth: 150,
28813     modal: true,
28814     proxyDrag: true,
28815     shadow: true
28816 });
28817 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28818 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28819 dlg.addButton('Cancel', dlg.hide, dlg);
28820 dlg.show();
28821 </code></pre>
28822   <b>A Dialog should always be a direct child of the body element.</b>
28823  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28824  * @cfg {String} title Default text to display in the title bar (defaults to null)
28825  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28826  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28827  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28828  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28829  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28830  * (defaults to null with no animation)
28831  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28832  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28833  * property for valid values (defaults to 'all')
28834  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28835  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28836  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28837  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28838  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28839  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28840  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28841  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28842  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28843  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28844  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28845  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28846  * draggable = true (defaults to false)
28847  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28848  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28849  * shadow (defaults to false)
28850  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28851  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28852  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28853  * @cfg {Array} buttons Array of buttons
28854  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28855  * @constructor
28856  * Create a new BasicDialog.
28857  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28858  * @param {Object} config Configuration options
28859  */
28860 Roo.BasicDialog = function(el, config){
28861     this.el = Roo.get(el);
28862     var dh = Roo.DomHelper;
28863     if(!this.el && config && config.autoCreate){
28864         if(typeof config.autoCreate == "object"){
28865             if(!config.autoCreate.id){
28866                 config.autoCreate.id = el;
28867             }
28868             this.el = dh.append(document.body,
28869                         config.autoCreate, true);
28870         }else{
28871             this.el = dh.append(document.body,
28872                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28873         }
28874     }
28875     el = this.el;
28876     el.setDisplayed(true);
28877     el.hide = this.hideAction;
28878     this.id = el.id;
28879     el.addClass("x-dlg");
28880
28881     Roo.apply(this, config);
28882
28883     this.proxy = el.createProxy("x-dlg-proxy");
28884     this.proxy.hide = this.hideAction;
28885     this.proxy.setOpacity(.5);
28886     this.proxy.hide();
28887
28888     if(config.width){
28889         el.setWidth(config.width);
28890     }
28891     if(config.height){
28892         el.setHeight(config.height);
28893     }
28894     this.size = el.getSize();
28895     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28896         this.xy = [config.x,config.y];
28897     }else{
28898         this.xy = el.getCenterXY(true);
28899     }
28900     /** The header element @type Roo.Element */
28901     this.header = el.child("> .x-dlg-hd");
28902     /** The body element @type Roo.Element */
28903     this.body = el.child("> .x-dlg-bd");
28904     /** The footer element @type Roo.Element */
28905     this.footer = el.child("> .x-dlg-ft");
28906
28907     if(!this.header){
28908         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28909     }
28910     if(!this.body){
28911         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28912     }
28913
28914     this.header.unselectable();
28915     if(this.title){
28916         this.header.update(this.title);
28917     }
28918     // this element allows the dialog to be focused for keyboard event
28919     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28920     this.focusEl.swallowEvent("click", true);
28921
28922     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28923
28924     // wrap the body and footer for special rendering
28925     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28926     if(this.footer){
28927         this.bwrap.dom.appendChild(this.footer.dom);
28928     }
28929
28930     this.bg = this.el.createChild({
28931         tag: "div", cls:"x-dlg-bg",
28932         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28933     });
28934     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28935
28936
28937     if(this.autoScroll !== false && !this.autoTabs){
28938         this.body.setStyle("overflow", "auto");
28939     }
28940
28941     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28942
28943     if(this.closable !== false){
28944         this.el.addClass("x-dlg-closable");
28945         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28946         this.close.on("click", this.closeClick, this);
28947         this.close.addClassOnOver("x-dlg-close-over");
28948     }
28949     if(this.collapsible !== false){
28950         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28951         this.collapseBtn.on("click", this.collapseClick, this);
28952         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28953         this.header.on("dblclick", this.collapseClick, this);
28954     }
28955     if(this.resizable !== false){
28956         this.el.addClass("x-dlg-resizable");
28957         this.resizer = new Roo.Resizable(el, {
28958             minWidth: this.minWidth || 80,
28959             minHeight:this.minHeight || 80,
28960             handles: this.resizeHandles || "all",
28961             pinned: true
28962         });
28963         this.resizer.on("beforeresize", this.beforeResize, this);
28964         this.resizer.on("resize", this.onResize, this);
28965     }
28966     if(this.draggable !== false){
28967         el.addClass("x-dlg-draggable");
28968         if (!this.proxyDrag) {
28969             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28970         }
28971         else {
28972             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28973         }
28974         dd.setHandleElId(this.header.id);
28975         dd.endDrag = this.endMove.createDelegate(this);
28976         dd.startDrag = this.startMove.createDelegate(this);
28977         dd.onDrag = this.onDrag.createDelegate(this);
28978         dd.scroll = false;
28979         this.dd = dd;
28980     }
28981     if(this.modal){
28982         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28983         this.mask.enableDisplayMode("block");
28984         this.mask.hide();
28985         this.el.addClass("x-dlg-modal");
28986     }
28987     if(this.shadow){
28988         this.shadow = new Roo.Shadow({
28989             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28990             offset : this.shadowOffset
28991         });
28992     }else{
28993         this.shadowOffset = 0;
28994     }
28995     if(Roo.useShims && this.shim !== false){
28996         this.shim = this.el.createShim();
28997         this.shim.hide = this.hideAction;
28998         this.shim.hide();
28999     }else{
29000         this.shim = false;
29001     }
29002     if(this.autoTabs){
29003         this.initTabs();
29004     }
29005     if (this.buttons) { 
29006         var bts= this.buttons;
29007         this.buttons = [];
29008         Roo.each(bts, function(b) {
29009             this.addButton(b);
29010         }, this);
29011     }
29012     
29013     
29014     this.addEvents({
29015         /**
29016          * @event keydown
29017          * Fires when a key is pressed
29018          * @param {Roo.BasicDialog} this
29019          * @param {Roo.EventObject} e
29020          */
29021         "keydown" : true,
29022         /**
29023          * @event move
29024          * Fires when this dialog is moved by the user.
29025          * @param {Roo.BasicDialog} this
29026          * @param {Number} x The new page X
29027          * @param {Number} y The new page Y
29028          */
29029         "move" : true,
29030         /**
29031          * @event resize
29032          * Fires when this dialog is resized by the user.
29033          * @param {Roo.BasicDialog} this
29034          * @param {Number} width The new width
29035          * @param {Number} height The new height
29036          */
29037         "resize" : true,
29038         /**
29039          * @event beforehide
29040          * Fires before this dialog is hidden.
29041          * @param {Roo.BasicDialog} this
29042          */
29043         "beforehide" : true,
29044         /**
29045          * @event hide
29046          * Fires when this dialog is hidden.
29047          * @param {Roo.BasicDialog} this
29048          */
29049         "hide" : true,
29050         /**
29051          * @event beforeshow
29052          * Fires before this dialog is shown.
29053          * @param {Roo.BasicDialog} this
29054          */
29055         "beforeshow" : true,
29056         /**
29057          * @event show
29058          * Fires when this dialog is shown.
29059          * @param {Roo.BasicDialog} this
29060          */
29061         "show" : true
29062     });
29063     el.on("keydown", this.onKeyDown, this);
29064     el.on("mousedown", this.toFront, this);
29065     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29066     this.el.hide();
29067     Roo.DialogManager.register(this);
29068     Roo.BasicDialog.superclass.constructor.call(this);
29069 };
29070
29071 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29072     shadowOffset: Roo.isIE ? 6 : 5,
29073     minHeight: 80,
29074     minWidth: 200,
29075     minButtonWidth: 75,
29076     defaultButton: null,
29077     buttonAlign: "right",
29078     tabTag: 'div',
29079     firstShow: true,
29080
29081     /**
29082      * Sets the dialog title text
29083      * @param {String} text The title text to display
29084      * @return {Roo.BasicDialog} this
29085      */
29086     setTitle : function(text){
29087         this.header.update(text);
29088         return this;
29089     },
29090
29091     // private
29092     closeClick : function(){
29093         this.hide();
29094     },
29095
29096     // private
29097     collapseClick : function(){
29098         this[this.collapsed ? "expand" : "collapse"]();
29099     },
29100
29101     /**
29102      * Collapses the dialog to its minimized state (only the title bar is visible).
29103      * Equivalent to the user clicking the collapse dialog button.
29104      */
29105     collapse : function(){
29106         if(!this.collapsed){
29107             this.collapsed = true;
29108             this.el.addClass("x-dlg-collapsed");
29109             this.restoreHeight = this.el.getHeight();
29110             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29111         }
29112     },
29113
29114     /**
29115      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29116      * clicking the expand dialog button.
29117      */
29118     expand : function(){
29119         if(this.collapsed){
29120             this.collapsed = false;
29121             this.el.removeClass("x-dlg-collapsed");
29122             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29123         }
29124     },
29125
29126     /**
29127      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29128      * @return {Roo.TabPanel} The tabs component
29129      */
29130     initTabs : function(){
29131         var tabs = this.getTabs();
29132         while(tabs.getTab(0)){
29133             tabs.removeTab(0);
29134         }
29135         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29136             var dom = el.dom;
29137             tabs.addTab(Roo.id(dom), dom.title);
29138             dom.title = "";
29139         });
29140         tabs.activate(0);
29141         return tabs;
29142     },
29143
29144     // private
29145     beforeResize : function(){
29146         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29147     },
29148
29149     // private
29150     onResize : function(){
29151         this.refreshSize();
29152         this.syncBodyHeight();
29153         this.adjustAssets();
29154         this.focus();
29155         this.fireEvent("resize", this, this.size.width, this.size.height);
29156     },
29157
29158     // private
29159     onKeyDown : function(e){
29160         if(this.isVisible()){
29161             this.fireEvent("keydown", this, e);
29162         }
29163     },
29164
29165     /**
29166      * Resizes the dialog.
29167      * @param {Number} width
29168      * @param {Number} height
29169      * @return {Roo.BasicDialog} this
29170      */
29171     resizeTo : function(width, height){
29172         this.el.setSize(width, height);
29173         this.size = {width: width, height: height};
29174         this.syncBodyHeight();
29175         if(this.fixedcenter){
29176             this.center();
29177         }
29178         if(this.isVisible()){
29179             this.constrainXY();
29180             this.adjustAssets();
29181         }
29182         this.fireEvent("resize", this, width, height);
29183         return this;
29184     },
29185
29186
29187     /**
29188      * Resizes the dialog to fit the specified content size.
29189      * @param {Number} width
29190      * @param {Number} height
29191      * @return {Roo.BasicDialog} this
29192      */
29193     setContentSize : function(w, h){
29194         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29195         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29196         //if(!this.el.isBorderBox()){
29197             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29198             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29199         //}
29200         if(this.tabs){
29201             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29202             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29203         }
29204         this.resizeTo(w, h);
29205         return this;
29206     },
29207
29208     /**
29209      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29210      * executed in response to a particular key being pressed while the dialog is active.
29211      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29212      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29213      * @param {Function} fn The function to call
29214      * @param {Object} scope (optional) The scope of the function
29215      * @return {Roo.BasicDialog} this
29216      */
29217     addKeyListener : function(key, fn, scope){
29218         var keyCode, shift, ctrl, alt;
29219         if(typeof key == "object" && !(key instanceof Array)){
29220             keyCode = key["key"];
29221             shift = key["shift"];
29222             ctrl = key["ctrl"];
29223             alt = key["alt"];
29224         }else{
29225             keyCode = key;
29226         }
29227         var handler = function(dlg, e){
29228             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29229                 var k = e.getKey();
29230                 if(keyCode instanceof Array){
29231                     for(var i = 0, len = keyCode.length; i < len; i++){
29232                         if(keyCode[i] == k){
29233                           fn.call(scope || window, dlg, k, e);
29234                           return;
29235                         }
29236                     }
29237                 }else{
29238                     if(k == keyCode){
29239                         fn.call(scope || window, dlg, k, e);
29240                     }
29241                 }
29242             }
29243         };
29244         this.on("keydown", handler);
29245         return this;
29246     },
29247
29248     /**
29249      * Returns the TabPanel component (creates it if it doesn't exist).
29250      * Note: If you wish to simply check for the existence of tabs without creating them,
29251      * check for a null 'tabs' property.
29252      * @return {Roo.TabPanel} The tabs component
29253      */
29254     getTabs : function(){
29255         if(!this.tabs){
29256             this.el.addClass("x-dlg-auto-tabs");
29257             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29258             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29259         }
29260         return this.tabs;
29261     },
29262
29263     /**
29264      * Adds a button to the footer section of the dialog.
29265      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29266      * object or a valid Roo.DomHelper element config
29267      * @param {Function} handler The function called when the button is clicked
29268      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29269      * @return {Roo.Button} The new button
29270      */
29271     addButton : function(config, handler, scope){
29272         var dh = Roo.DomHelper;
29273         if(!this.footer){
29274             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29275         }
29276         if(!this.btnContainer){
29277             var tb = this.footer.createChild({
29278
29279                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29280                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29281             }, null, true);
29282             this.btnContainer = tb.firstChild.firstChild.firstChild;
29283         }
29284         var bconfig = {
29285             handler: handler,
29286             scope: scope,
29287             minWidth: this.minButtonWidth,
29288             hideParent:true
29289         };
29290         if(typeof config == "string"){
29291             bconfig.text = config;
29292         }else{
29293             if(config.tag){
29294                 bconfig.dhconfig = config;
29295             }else{
29296                 Roo.apply(bconfig, config);
29297             }
29298         }
29299         var fc = false;
29300         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29301             bconfig.position = Math.max(0, bconfig.position);
29302             fc = this.btnContainer.childNodes[bconfig.position];
29303         }
29304          
29305         var btn = new Roo.Button(
29306             fc ? 
29307                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29308                 : this.btnContainer.appendChild(document.createElement("td")),
29309             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29310             bconfig
29311         );
29312         this.syncBodyHeight();
29313         if(!this.buttons){
29314             /**
29315              * Array of all the buttons that have been added to this dialog via addButton
29316              * @type Array
29317              */
29318             this.buttons = [];
29319         }
29320         this.buttons.push(btn);
29321         return btn;
29322     },
29323
29324     /**
29325      * Sets the default button to be focused when the dialog is displayed.
29326      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29327      * @return {Roo.BasicDialog} this
29328      */
29329     setDefaultButton : function(btn){
29330         this.defaultButton = btn;
29331         return this;
29332     },
29333
29334     // private
29335     getHeaderFooterHeight : function(safe){
29336         var height = 0;
29337         if(this.header){
29338            height += this.header.getHeight();
29339         }
29340         if(this.footer){
29341            var fm = this.footer.getMargins();
29342             height += (this.footer.getHeight()+fm.top+fm.bottom);
29343         }
29344         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29345         height += this.centerBg.getPadding("tb");
29346         return height;
29347     },
29348
29349     // private
29350     syncBodyHeight : function(){
29351         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29352         var height = this.size.height - this.getHeaderFooterHeight(false);
29353         bd.setHeight(height-bd.getMargins("tb"));
29354         var hh = this.header.getHeight();
29355         var h = this.size.height-hh;
29356         cb.setHeight(h);
29357         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29358         bw.setHeight(h-cb.getPadding("tb"));
29359         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29360         bd.setWidth(bw.getWidth(true));
29361         if(this.tabs){
29362             this.tabs.syncHeight();
29363             if(Roo.isIE){
29364                 this.tabs.el.repaint();
29365             }
29366         }
29367     },
29368
29369     /**
29370      * Restores the previous state of the dialog if Roo.state is configured.
29371      * @return {Roo.BasicDialog} this
29372      */
29373     restoreState : function(){
29374         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29375         if(box && box.width){
29376             this.xy = [box.x, box.y];
29377             this.resizeTo(box.width, box.height);
29378         }
29379         return this;
29380     },
29381
29382     // private
29383     beforeShow : function(){
29384         this.expand();
29385         if(this.fixedcenter){
29386             this.xy = this.el.getCenterXY(true);
29387         }
29388         if(this.modal){
29389             Roo.get(document.body).addClass("x-body-masked");
29390             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29391             this.mask.show();
29392         }
29393         this.constrainXY();
29394     },
29395
29396     // private
29397     animShow : function(){
29398         var b = Roo.get(this.animateTarget).getBox();
29399         this.proxy.setSize(b.width, b.height);
29400         this.proxy.setLocation(b.x, b.y);
29401         this.proxy.show();
29402         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29403                     true, .35, this.showEl.createDelegate(this));
29404     },
29405
29406     /**
29407      * Shows the dialog.
29408      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29409      * @return {Roo.BasicDialog} this
29410      */
29411     show : function(animateTarget){
29412         if (this.fireEvent("beforeshow", this) === false){
29413             return;
29414         }
29415         if(this.syncHeightBeforeShow){
29416             this.syncBodyHeight();
29417         }else if(this.firstShow){
29418             this.firstShow = false;
29419             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29420         }
29421         this.animateTarget = animateTarget || this.animateTarget;
29422         if(!this.el.isVisible()){
29423             this.beforeShow();
29424             if(this.animateTarget && Roo.get(this.animateTarget)){
29425                 this.animShow();
29426             }else{
29427                 this.showEl();
29428             }
29429         }
29430         return this;
29431     },
29432
29433     // private
29434     showEl : function(){
29435         this.proxy.hide();
29436         this.el.setXY(this.xy);
29437         this.el.show();
29438         this.adjustAssets(true);
29439         this.toFront();
29440         this.focus();
29441         // IE peekaboo bug - fix found by Dave Fenwick
29442         if(Roo.isIE){
29443             this.el.repaint();
29444         }
29445         this.fireEvent("show", this);
29446     },
29447
29448     /**
29449      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29450      * dialog itself will receive focus.
29451      */
29452     focus : function(){
29453         if(this.defaultButton){
29454             this.defaultButton.focus();
29455         }else{
29456             this.focusEl.focus();
29457         }
29458     },
29459
29460     // private
29461     constrainXY : function(){
29462         if(this.constraintoviewport !== false){
29463             if(!this.viewSize){
29464                 if(this.container){
29465                     var s = this.container.getSize();
29466                     this.viewSize = [s.width, s.height];
29467                 }else{
29468                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29469                 }
29470             }
29471             var s = Roo.get(this.container||document).getScroll();
29472
29473             var x = this.xy[0], y = this.xy[1];
29474             var w = this.size.width, h = this.size.height;
29475             var vw = this.viewSize[0], vh = this.viewSize[1];
29476             // only move it if it needs it
29477             var moved = false;
29478             // first validate right/bottom
29479             if(x + w > vw+s.left){
29480                 x = vw - w;
29481                 moved = true;
29482             }
29483             if(y + h > vh+s.top){
29484                 y = vh - h;
29485                 moved = true;
29486             }
29487             // then make sure top/left isn't negative
29488             if(x < s.left){
29489                 x = s.left;
29490                 moved = true;
29491             }
29492             if(y < s.top){
29493                 y = s.top;
29494                 moved = true;
29495             }
29496             if(moved){
29497                 // cache xy
29498                 this.xy = [x, y];
29499                 if(this.isVisible()){
29500                     this.el.setLocation(x, y);
29501                     this.adjustAssets();
29502                 }
29503             }
29504         }
29505     },
29506
29507     // private
29508     onDrag : function(){
29509         if(!this.proxyDrag){
29510             this.xy = this.el.getXY();
29511             this.adjustAssets();
29512         }
29513     },
29514
29515     // private
29516     adjustAssets : function(doShow){
29517         var x = this.xy[0], y = this.xy[1];
29518         var w = this.size.width, h = this.size.height;
29519         if(doShow === true){
29520             if(this.shadow){
29521                 this.shadow.show(this.el);
29522             }
29523             if(this.shim){
29524                 this.shim.show();
29525             }
29526         }
29527         if(this.shadow && this.shadow.isVisible()){
29528             this.shadow.show(this.el);
29529         }
29530         if(this.shim && this.shim.isVisible()){
29531             this.shim.setBounds(x, y, w, h);
29532         }
29533     },
29534
29535     // private
29536     adjustViewport : function(w, h){
29537         if(!w || !h){
29538             w = Roo.lib.Dom.getViewWidth();
29539             h = Roo.lib.Dom.getViewHeight();
29540         }
29541         // cache the size
29542         this.viewSize = [w, h];
29543         if(this.modal && this.mask.isVisible()){
29544             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29545             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29546         }
29547         if(this.isVisible()){
29548             this.constrainXY();
29549         }
29550     },
29551
29552     /**
29553      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29554      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29555      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29556      */
29557     destroy : function(removeEl){
29558         if(this.isVisible()){
29559             this.animateTarget = null;
29560             this.hide();
29561         }
29562         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29563         if(this.tabs){
29564             this.tabs.destroy(removeEl);
29565         }
29566         Roo.destroy(
29567              this.shim,
29568              this.proxy,
29569              this.resizer,
29570              this.close,
29571              this.mask
29572         );
29573         if(this.dd){
29574             this.dd.unreg();
29575         }
29576         if(this.buttons){
29577            for(var i = 0, len = this.buttons.length; i < len; i++){
29578                this.buttons[i].destroy();
29579            }
29580         }
29581         this.el.removeAllListeners();
29582         if(removeEl === true){
29583             this.el.update("");
29584             this.el.remove();
29585         }
29586         Roo.DialogManager.unregister(this);
29587     },
29588
29589     // private
29590     startMove : function(){
29591         if(this.proxyDrag){
29592             this.proxy.show();
29593         }
29594         if(this.constraintoviewport !== false){
29595             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29596         }
29597     },
29598
29599     // private
29600     endMove : function(){
29601         if(!this.proxyDrag){
29602             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29603         }else{
29604             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29605             this.proxy.hide();
29606         }
29607         this.refreshSize();
29608         this.adjustAssets();
29609         this.focus();
29610         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29611     },
29612
29613     /**
29614      * Brings this dialog to the front of any other visible dialogs
29615      * @return {Roo.BasicDialog} this
29616      */
29617     toFront : function(){
29618         Roo.DialogManager.bringToFront(this);
29619         return this;
29620     },
29621
29622     /**
29623      * Sends this dialog to the back (under) of any other visible dialogs
29624      * @return {Roo.BasicDialog} this
29625      */
29626     toBack : function(){
29627         Roo.DialogManager.sendToBack(this);
29628         return this;
29629     },
29630
29631     /**
29632      * Centers this dialog in the viewport
29633      * @return {Roo.BasicDialog} this
29634      */
29635     center : function(){
29636         var xy = this.el.getCenterXY(true);
29637         this.moveTo(xy[0], xy[1]);
29638         return this;
29639     },
29640
29641     /**
29642      * Moves the dialog's top-left corner to the specified point
29643      * @param {Number} x
29644      * @param {Number} y
29645      * @return {Roo.BasicDialog} this
29646      */
29647     moveTo : function(x, y){
29648         this.xy = [x,y];
29649         if(this.isVisible()){
29650             this.el.setXY(this.xy);
29651             this.adjustAssets();
29652         }
29653         return this;
29654     },
29655
29656     /**
29657      * Aligns the dialog to the specified element
29658      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29659      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29660      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29661      * @return {Roo.BasicDialog} this
29662      */
29663     alignTo : function(element, position, offsets){
29664         this.xy = this.el.getAlignToXY(element, position, offsets);
29665         if(this.isVisible()){
29666             this.el.setXY(this.xy);
29667             this.adjustAssets();
29668         }
29669         return this;
29670     },
29671
29672     /**
29673      * Anchors an element to another element and realigns it when the window is resized.
29674      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29675      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29676      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29677      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29678      * is a number, it is used as the buffer delay (defaults to 50ms).
29679      * @return {Roo.BasicDialog} this
29680      */
29681     anchorTo : function(el, alignment, offsets, monitorScroll){
29682         var action = function(){
29683             this.alignTo(el, alignment, offsets);
29684         };
29685         Roo.EventManager.onWindowResize(action, this);
29686         var tm = typeof monitorScroll;
29687         if(tm != 'undefined'){
29688             Roo.EventManager.on(window, 'scroll', action, this,
29689                 {buffer: tm == 'number' ? monitorScroll : 50});
29690         }
29691         action.call(this);
29692         return this;
29693     },
29694
29695     /**
29696      * Returns true if the dialog is visible
29697      * @return {Boolean}
29698      */
29699     isVisible : function(){
29700         return this.el.isVisible();
29701     },
29702
29703     // private
29704     animHide : function(callback){
29705         var b = Roo.get(this.animateTarget).getBox();
29706         this.proxy.show();
29707         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29708         this.el.hide();
29709         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29710                     this.hideEl.createDelegate(this, [callback]));
29711     },
29712
29713     /**
29714      * Hides the dialog.
29715      * @param {Function} callback (optional) Function to call when the dialog is hidden
29716      * @return {Roo.BasicDialog} this
29717      */
29718     hide : function(callback){
29719         if (this.fireEvent("beforehide", this) === false){
29720             return;
29721         }
29722         if(this.shadow){
29723             this.shadow.hide();
29724         }
29725         if(this.shim) {
29726           this.shim.hide();
29727         }
29728         // sometimes animateTarget seems to get set.. causing problems...
29729         // this just double checks..
29730         if(this.animateTarget && Roo.get(this.animateTarget)) {
29731            this.animHide(callback);
29732         }else{
29733             this.el.hide();
29734             this.hideEl(callback);
29735         }
29736         return this;
29737     },
29738
29739     // private
29740     hideEl : function(callback){
29741         this.proxy.hide();
29742         if(this.modal){
29743             this.mask.hide();
29744             Roo.get(document.body).removeClass("x-body-masked");
29745         }
29746         this.fireEvent("hide", this);
29747         if(typeof callback == "function"){
29748             callback();
29749         }
29750     },
29751
29752     // private
29753     hideAction : function(){
29754         this.setLeft("-10000px");
29755         this.setTop("-10000px");
29756         this.setStyle("visibility", "hidden");
29757     },
29758
29759     // private
29760     refreshSize : function(){
29761         this.size = this.el.getSize();
29762         this.xy = this.el.getXY();
29763         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29764     },
29765
29766     // private
29767     // z-index is managed by the DialogManager and may be overwritten at any time
29768     setZIndex : function(index){
29769         if(this.modal){
29770             this.mask.setStyle("z-index", index);
29771         }
29772         if(this.shim){
29773             this.shim.setStyle("z-index", ++index);
29774         }
29775         if(this.shadow){
29776             this.shadow.setZIndex(++index);
29777         }
29778         this.el.setStyle("z-index", ++index);
29779         if(this.proxy){
29780             this.proxy.setStyle("z-index", ++index);
29781         }
29782         if(this.resizer){
29783             this.resizer.proxy.setStyle("z-index", ++index);
29784         }
29785
29786         this.lastZIndex = index;
29787     },
29788
29789     /**
29790      * Returns the element for this dialog
29791      * @return {Roo.Element} The underlying dialog Element
29792      */
29793     getEl : function(){
29794         return this.el;
29795     }
29796 });
29797
29798 /**
29799  * @class Roo.DialogManager
29800  * Provides global access to BasicDialogs that have been created and
29801  * support for z-indexing (layering) multiple open dialogs.
29802  */
29803 Roo.DialogManager = function(){
29804     var list = {};
29805     var accessList = [];
29806     var front = null;
29807
29808     // private
29809     var sortDialogs = function(d1, d2){
29810         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29811     };
29812
29813     // private
29814     var orderDialogs = function(){
29815         accessList.sort(sortDialogs);
29816         var seed = Roo.DialogManager.zseed;
29817         for(var i = 0, len = accessList.length; i < len; i++){
29818             var dlg = accessList[i];
29819             if(dlg){
29820                 dlg.setZIndex(seed + (i*10));
29821             }
29822         }
29823     };
29824
29825     return {
29826         /**
29827          * The starting z-index for BasicDialogs (defaults to 9000)
29828          * @type Number The z-index value
29829          */
29830         zseed : 9000,
29831
29832         // private
29833         register : function(dlg){
29834             list[dlg.id] = dlg;
29835             accessList.push(dlg);
29836         },
29837
29838         // private
29839         unregister : function(dlg){
29840             delete list[dlg.id];
29841             var i=0;
29842             var len=0;
29843             if(!accessList.indexOf){
29844                 for(  i = 0, len = accessList.length; i < len; i++){
29845                     if(accessList[i] == dlg){
29846                         accessList.splice(i, 1);
29847                         return;
29848                     }
29849                 }
29850             }else{
29851                  i = accessList.indexOf(dlg);
29852                 if(i != -1){
29853                     accessList.splice(i, 1);
29854                 }
29855             }
29856         },
29857
29858         /**
29859          * Gets a registered dialog by id
29860          * @param {String/Object} id The id of the dialog or a dialog
29861          * @return {Roo.BasicDialog} this
29862          */
29863         get : function(id){
29864             return typeof id == "object" ? id : list[id];
29865         },
29866
29867         /**
29868          * Brings the specified dialog to the front
29869          * @param {String/Object} dlg The id of the dialog or a dialog
29870          * @return {Roo.BasicDialog} this
29871          */
29872         bringToFront : function(dlg){
29873             dlg = this.get(dlg);
29874             if(dlg != front){
29875                 front = dlg;
29876                 dlg._lastAccess = new Date().getTime();
29877                 orderDialogs();
29878             }
29879             return dlg;
29880         },
29881
29882         /**
29883          * Sends the specified dialog to the back
29884          * @param {String/Object} dlg The id of the dialog or a dialog
29885          * @return {Roo.BasicDialog} this
29886          */
29887         sendToBack : function(dlg){
29888             dlg = this.get(dlg);
29889             dlg._lastAccess = -(new Date().getTime());
29890             orderDialogs();
29891             return dlg;
29892         },
29893
29894         /**
29895          * Hides all dialogs
29896          */
29897         hideAll : function(){
29898             for(var id in list){
29899                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29900                     list[id].hide();
29901                 }
29902             }
29903         }
29904     };
29905 }();
29906
29907 /**
29908  * @class Roo.LayoutDialog
29909  * @extends Roo.BasicDialog
29910  * Dialog which provides adjustments for working with a layout in a Dialog.
29911  * Add your necessary layout config options to the dialog's config.<br>
29912  * Example usage (including a nested layout):
29913  * <pre><code>
29914 if(!dialog){
29915     dialog = new Roo.LayoutDialog("download-dlg", {
29916         modal: true,
29917         width:600,
29918         height:450,
29919         shadow:true,
29920         minWidth:500,
29921         minHeight:350,
29922         autoTabs:true,
29923         proxyDrag:true,
29924         // layout config merges with the dialog config
29925         center:{
29926             tabPosition: "top",
29927             alwaysShowTabs: true
29928         }
29929     });
29930     dialog.addKeyListener(27, dialog.hide, dialog);
29931     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29932     dialog.addButton("Build It!", this.getDownload, this);
29933
29934     // we can even add nested layouts
29935     var innerLayout = new Roo.BorderLayout("dl-inner", {
29936         east: {
29937             initialSize: 200,
29938             autoScroll:true,
29939             split:true
29940         },
29941         center: {
29942             autoScroll:true
29943         }
29944     });
29945     innerLayout.beginUpdate();
29946     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29947     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29948     innerLayout.endUpdate(true);
29949
29950     var layout = dialog.getLayout();
29951     layout.beginUpdate();
29952     layout.add("center", new Roo.ContentPanel("standard-panel",
29953                         {title: "Download the Source", fitToFrame:true}));
29954     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29955                {title: "Build your own roo.js"}));
29956     layout.getRegion("center").showPanel(sp);
29957     layout.endUpdate();
29958 }
29959 </code></pre>
29960     * @constructor
29961     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29962     * @param {Object} config configuration options
29963   */
29964 Roo.LayoutDialog = function(el, cfg){
29965     
29966     var config=  cfg;
29967     if (typeof(cfg) == 'undefined') {
29968         config = Roo.apply({}, el);
29969         // not sure why we use documentElement here.. - it should always be body.
29970         // IE7 borks horribly if we use documentElement.
29971         // webkit also does not like documentElement - it creates a body element...
29972         el = Roo.get( document.body || document.documentElement ).createChild();
29973         //config.autoCreate = true;
29974     }
29975     
29976     
29977     config.autoTabs = false;
29978     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29979     this.body.setStyle({overflow:"hidden", position:"relative"});
29980     this.layout = new Roo.BorderLayout(this.body.dom, config);
29981     this.layout.monitorWindowResize = false;
29982     this.el.addClass("x-dlg-auto-layout");
29983     // fix case when center region overwrites center function
29984     this.center = Roo.BasicDialog.prototype.center;
29985     this.on("show", this.layout.layout, this.layout, true);
29986     if (config.items) {
29987         var xitems = config.items;
29988         delete config.items;
29989         Roo.each(xitems, this.addxtype, this);
29990     }
29991     
29992     
29993 };
29994 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29995     /**
29996      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29997      * @deprecated
29998      */
29999     endUpdate : function(){
30000         this.layout.endUpdate();
30001     },
30002
30003     /**
30004      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30005      *  @deprecated
30006      */
30007     beginUpdate : function(){
30008         this.layout.beginUpdate();
30009     },
30010
30011     /**
30012      * Get the BorderLayout for this dialog
30013      * @return {Roo.BorderLayout}
30014      */
30015     getLayout : function(){
30016         return this.layout;
30017     },
30018
30019     showEl : function(){
30020         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30021         if(Roo.isIE7){
30022             this.layout.layout();
30023         }
30024     },
30025
30026     // private
30027     // Use the syncHeightBeforeShow config option to control this automatically
30028     syncBodyHeight : function(){
30029         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30030         if(this.layout){this.layout.layout();}
30031     },
30032     
30033       /**
30034      * Add an xtype element (actually adds to the layout.)
30035      * @return {Object} xdata xtype object data.
30036      */
30037     
30038     addxtype : function(c) {
30039         return this.layout.addxtype(c);
30040     }
30041 });/*
30042  * Based on:
30043  * Ext JS Library 1.1.1
30044  * Copyright(c) 2006-2007, Ext JS, LLC.
30045  *
30046  * Originally Released Under LGPL - original licence link has changed is not relivant.
30047  *
30048  * Fork - LGPL
30049  * <script type="text/javascript">
30050  */
30051  
30052 /**
30053  * @class Roo.MessageBox
30054  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30055  * Example usage:
30056  *<pre><code>
30057 // Basic alert:
30058 Roo.Msg.alert('Status', 'Changes saved successfully.');
30059
30060 // Prompt for user data:
30061 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30062     if (btn == 'ok'){
30063         // process text value...
30064     }
30065 });
30066
30067 // Show a dialog using config options:
30068 Roo.Msg.show({
30069    title:'Save Changes?',
30070    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30071    buttons: Roo.Msg.YESNOCANCEL,
30072    fn: processResult,
30073    animEl: 'elId'
30074 });
30075 </code></pre>
30076  * @singleton
30077  */
30078 Roo.MessageBox = function(){
30079     var dlg, opt, mask, waitTimer;
30080     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30081     var buttons, activeTextEl, bwidth;
30082
30083     // private
30084     var handleButton = function(button){
30085         dlg.hide();
30086         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30087     };
30088
30089     // private
30090     var handleHide = function(){
30091         if(opt && opt.cls){
30092             dlg.el.removeClass(opt.cls);
30093         }
30094         if(waitTimer){
30095             Roo.TaskMgr.stop(waitTimer);
30096             waitTimer = null;
30097         }
30098     };
30099
30100     // private
30101     var updateButtons = function(b){
30102         var width = 0;
30103         if(!b){
30104             buttons["ok"].hide();
30105             buttons["cancel"].hide();
30106             buttons["yes"].hide();
30107             buttons["no"].hide();
30108             dlg.footer.dom.style.display = 'none';
30109             return width;
30110         }
30111         dlg.footer.dom.style.display = '';
30112         for(var k in buttons){
30113             if(typeof buttons[k] != "function"){
30114                 if(b[k]){
30115                     buttons[k].show();
30116                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30117                     width += buttons[k].el.getWidth()+15;
30118                 }else{
30119                     buttons[k].hide();
30120                 }
30121             }
30122         }
30123         return width;
30124     };
30125
30126     // private
30127     var handleEsc = function(d, k, e){
30128         if(opt && opt.closable !== false){
30129             dlg.hide();
30130         }
30131         if(e){
30132             e.stopEvent();
30133         }
30134     };
30135
30136     return {
30137         /**
30138          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30139          * @return {Roo.BasicDialog} The BasicDialog element
30140          */
30141         getDialog : function(){
30142            if(!dlg){
30143                 dlg = new Roo.BasicDialog("x-msg-box", {
30144                     autoCreate : true,
30145                     shadow: true,
30146                     draggable: true,
30147                     resizable:false,
30148                     constraintoviewport:false,
30149                     fixedcenter:true,
30150                     collapsible : false,
30151                     shim:true,
30152                     modal: true,
30153                     width:400, height:100,
30154                     buttonAlign:"center",
30155                     closeClick : function(){
30156                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30157                             handleButton("no");
30158                         }else{
30159                             handleButton("cancel");
30160                         }
30161                     }
30162                 });
30163                 dlg.on("hide", handleHide);
30164                 mask = dlg.mask;
30165                 dlg.addKeyListener(27, handleEsc);
30166                 buttons = {};
30167                 var bt = this.buttonText;
30168                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30169                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30170                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30171                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30172                 bodyEl = dlg.body.createChild({
30173
30174                     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>'
30175                 });
30176                 msgEl = bodyEl.dom.firstChild;
30177                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30178                 textboxEl.enableDisplayMode();
30179                 textboxEl.addKeyListener([10,13], function(){
30180                     if(dlg.isVisible() && opt && opt.buttons){
30181                         if(opt.buttons.ok){
30182                             handleButton("ok");
30183                         }else if(opt.buttons.yes){
30184                             handleButton("yes");
30185                         }
30186                     }
30187                 });
30188                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30189                 textareaEl.enableDisplayMode();
30190                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30191                 progressEl.enableDisplayMode();
30192                 var pf = progressEl.dom.firstChild;
30193                 if (pf) {
30194                     pp = Roo.get(pf.firstChild);
30195                     pp.setHeight(pf.offsetHeight);
30196                 }
30197                 
30198             }
30199             return dlg;
30200         },
30201
30202         /**
30203          * Updates the message box body text
30204          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30205          * the XHTML-compliant non-breaking space character '&amp;#160;')
30206          * @return {Roo.MessageBox} This message box
30207          */
30208         updateText : function(text){
30209             if(!dlg.isVisible() && !opt.width){
30210                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30211             }
30212             msgEl.innerHTML = text || '&#160;';
30213       
30214             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30215             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30216             var w = Math.max(
30217                     Math.min(opt.width || cw , this.maxWidth), 
30218                     Math.max(opt.minWidth || this.minWidth, bwidth)
30219             );
30220             if(opt.prompt){
30221                 activeTextEl.setWidth(w);
30222             }
30223             if(dlg.isVisible()){
30224                 dlg.fixedcenter = false;
30225             }
30226             // to big, make it scroll. = But as usual stupid IE does not support
30227             // !important..
30228             
30229             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30230                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30231                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30232             } else {
30233                 bodyEl.dom.style.height = '';
30234                 bodyEl.dom.style.overflowY = '';
30235             }
30236             if (cw > w) {
30237                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30238             } else {
30239                 bodyEl.dom.style.overflowX = '';
30240             }
30241             
30242             dlg.setContentSize(w, bodyEl.getHeight());
30243             if(dlg.isVisible()){
30244                 dlg.fixedcenter = true;
30245             }
30246             return this;
30247         },
30248
30249         /**
30250          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30251          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30252          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30253          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30254          * @return {Roo.MessageBox} This message box
30255          */
30256         updateProgress : function(value, text){
30257             if(text){
30258                 this.updateText(text);
30259             }
30260             if (pp) { // weird bug on my firefox - for some reason this is not defined
30261                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30262             }
30263             return this;
30264         },        
30265
30266         /**
30267          * Returns true if the message box is currently displayed
30268          * @return {Boolean} True if the message box is visible, else false
30269          */
30270         isVisible : function(){
30271             return dlg && dlg.isVisible();  
30272         },
30273
30274         /**
30275          * Hides the message box if it is displayed
30276          */
30277         hide : function(){
30278             if(this.isVisible()){
30279                 dlg.hide();
30280             }  
30281         },
30282
30283         /**
30284          * Displays a new message box, or reinitializes an existing message box, based on the config options
30285          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30286          * The following config object properties are supported:
30287          * <pre>
30288 Property    Type             Description
30289 ----------  ---------------  ------------------------------------------------------------------------------------
30290 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30291                                    closes (defaults to undefined)
30292 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30293                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30294 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30295                                    progress and wait dialogs will ignore this property and always hide the
30296                                    close button as they can only be closed programmatically.
30297 cls               String           A custom CSS class to apply to the message box element
30298 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30299                                    displayed (defaults to 75)
30300 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30301                                    function will be btn (the name of the button that was clicked, if applicable,
30302                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30303                                    Progress and wait dialogs will ignore this option since they do not respond to
30304                                    user actions and can only be closed programmatically, so any required function
30305                                    should be called by the same code after it closes the dialog.
30306 icon              String           A CSS class that provides a background image to be used as an icon for
30307                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30308 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30309 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30310 modal             Boolean          False to allow user interaction with the page while the message box is
30311                                    displayed (defaults to true)
30312 msg               String           A string that will replace the existing message box body text (defaults
30313                                    to the XHTML-compliant non-breaking space character '&#160;')
30314 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30315 progress          Boolean          True to display a progress bar (defaults to false)
30316 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30317 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30318 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30319 title             String           The title text
30320 value             String           The string value to set into the active textbox element if displayed
30321 wait              Boolean          True to display a progress bar (defaults to false)
30322 width             Number           The width of the dialog in pixels
30323 </pre>
30324          *
30325          * Example usage:
30326          * <pre><code>
30327 Roo.Msg.show({
30328    title: 'Address',
30329    msg: 'Please enter your address:',
30330    width: 300,
30331    buttons: Roo.MessageBox.OKCANCEL,
30332    multiline: true,
30333    fn: saveAddress,
30334    animEl: 'addAddressBtn'
30335 });
30336 </code></pre>
30337          * @param {Object} config Configuration options
30338          * @return {Roo.MessageBox} This message box
30339          */
30340         show : function(options)
30341         {
30342             
30343             // this causes nightmares if you show one dialog after another
30344             // especially on callbacks..
30345              
30346             if(this.isVisible()){
30347                 
30348                 this.hide();
30349                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30350                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30351                 Roo.log("New Dialog Message:" +  options.msg )
30352                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30353                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30354                 
30355             }
30356             var d = this.getDialog();
30357             opt = options;
30358             d.setTitle(opt.title || "&#160;");
30359             d.close.setDisplayed(opt.closable !== false);
30360             activeTextEl = textboxEl;
30361             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30362             if(opt.prompt){
30363                 if(opt.multiline){
30364                     textboxEl.hide();
30365                     textareaEl.show();
30366                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30367                         opt.multiline : this.defaultTextHeight);
30368                     activeTextEl = textareaEl;
30369                 }else{
30370                     textboxEl.show();
30371                     textareaEl.hide();
30372                 }
30373             }else{
30374                 textboxEl.hide();
30375                 textareaEl.hide();
30376             }
30377             progressEl.setDisplayed(opt.progress === true);
30378             this.updateProgress(0);
30379             activeTextEl.dom.value = opt.value || "";
30380             if(opt.prompt){
30381                 dlg.setDefaultButton(activeTextEl);
30382             }else{
30383                 var bs = opt.buttons;
30384                 var db = null;
30385                 if(bs && bs.ok){
30386                     db = buttons["ok"];
30387                 }else if(bs && bs.yes){
30388                     db = buttons["yes"];
30389                 }
30390                 dlg.setDefaultButton(db);
30391             }
30392             bwidth = updateButtons(opt.buttons);
30393             this.updateText(opt.msg);
30394             if(opt.cls){
30395                 d.el.addClass(opt.cls);
30396             }
30397             d.proxyDrag = opt.proxyDrag === true;
30398             d.modal = opt.modal !== false;
30399             d.mask = opt.modal !== false ? mask : false;
30400             if(!d.isVisible()){
30401                 // force it to the end of the z-index stack so it gets a cursor in FF
30402                 document.body.appendChild(dlg.el.dom);
30403                 d.animateTarget = null;
30404                 d.show(options.animEl);
30405             }
30406             return this;
30407         },
30408
30409         /**
30410          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30411          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30412          * and closing the message box when the process is complete.
30413          * @param {String} title The title bar text
30414          * @param {String} msg The message box body text
30415          * @return {Roo.MessageBox} This message box
30416          */
30417         progress : function(title, msg){
30418             this.show({
30419                 title : title,
30420                 msg : msg,
30421                 buttons: false,
30422                 progress:true,
30423                 closable:false,
30424                 minWidth: this.minProgressWidth,
30425                 modal : true
30426             });
30427             return this;
30428         },
30429
30430         /**
30431          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30432          * If a callback function is passed it will be called after the user clicks the button, and the
30433          * id of the button that was clicked will be passed as the only parameter to the callback
30434          * (could also be the top-right close button).
30435          * @param {String} title The title bar text
30436          * @param {String} msg The message box body text
30437          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30438          * @param {Object} scope (optional) The scope of the callback function
30439          * @return {Roo.MessageBox} This message box
30440          */
30441         alert : function(title, msg, fn, scope){
30442             this.show({
30443                 title : title,
30444                 msg : msg,
30445                 buttons: this.OK,
30446                 fn: fn,
30447                 scope : scope,
30448                 modal : true
30449             });
30450             return this;
30451         },
30452
30453         /**
30454          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30455          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30456          * You are responsible for closing the message box when the process is complete.
30457          * @param {String} msg The message box body text
30458          * @param {String} title (optional) The title bar text
30459          * @return {Roo.MessageBox} This message box
30460          */
30461         wait : function(msg, title){
30462             this.show({
30463                 title : title,
30464                 msg : msg,
30465                 buttons: false,
30466                 closable:false,
30467                 progress:true,
30468                 modal:true,
30469                 width:300,
30470                 wait:true
30471             });
30472             waitTimer = Roo.TaskMgr.start({
30473                 run: function(i){
30474                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30475                 },
30476                 interval: 1000
30477             });
30478             return this;
30479         },
30480
30481         /**
30482          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30483          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30484          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30485          * @param {String} title The title bar text
30486          * @param {String} msg The message box body text
30487          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30488          * @param {Object} scope (optional) The scope of the callback function
30489          * @return {Roo.MessageBox} This message box
30490          */
30491         confirm : function(title, msg, fn, scope){
30492             this.show({
30493                 title : title,
30494                 msg : msg,
30495                 buttons: this.YESNO,
30496                 fn: fn,
30497                 scope : scope,
30498                 modal : true
30499             });
30500             return this;
30501         },
30502
30503         /**
30504          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30505          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30506          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30507          * (could also be the top-right close button) and the text that was entered will be passed as the two
30508          * parameters to the callback.
30509          * @param {String} title The title bar text
30510          * @param {String} msg The message box body text
30511          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30512          * @param {Object} scope (optional) The scope of the callback function
30513          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30514          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30515          * @return {Roo.MessageBox} This message box
30516          */
30517         prompt : function(title, msg, fn, scope, multiline){
30518             this.show({
30519                 title : title,
30520                 msg : msg,
30521                 buttons: this.OKCANCEL,
30522                 fn: fn,
30523                 minWidth:250,
30524                 scope : scope,
30525                 prompt:true,
30526                 multiline: multiline,
30527                 modal : true
30528             });
30529             return this;
30530         },
30531
30532         /**
30533          * Button config that displays a single OK button
30534          * @type Object
30535          */
30536         OK : {ok:true},
30537         /**
30538          * Button config that displays Yes and No buttons
30539          * @type Object
30540          */
30541         YESNO : {yes:true, no:true},
30542         /**
30543          * Button config that displays OK and Cancel buttons
30544          * @type Object
30545          */
30546         OKCANCEL : {ok:true, cancel:true},
30547         /**
30548          * Button config that displays Yes, No and Cancel buttons
30549          * @type Object
30550          */
30551         YESNOCANCEL : {yes:true, no:true, cancel:true},
30552
30553         /**
30554          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30555          * @type Number
30556          */
30557         defaultTextHeight : 75,
30558         /**
30559          * The maximum width in pixels of the message box (defaults to 600)
30560          * @type Number
30561          */
30562         maxWidth : 600,
30563         /**
30564          * The minimum width in pixels of the message box (defaults to 100)
30565          * @type Number
30566          */
30567         minWidth : 100,
30568         /**
30569          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30570          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30571          * @type Number
30572          */
30573         minProgressWidth : 250,
30574         /**
30575          * An object containing the default button text strings that can be overriden for localized language support.
30576          * Supported properties are: ok, cancel, yes and no.
30577          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30578          * @type Object
30579          */
30580         buttonText : {
30581             ok : "OK",
30582             cancel : "Cancel",
30583             yes : "Yes",
30584             no : "No"
30585         }
30586     };
30587 }();
30588
30589 /**
30590  * Shorthand for {@link Roo.MessageBox}
30591  */
30592 Roo.Msg = Roo.MessageBox;/*
30593  * Based on:
30594  * Ext JS Library 1.1.1
30595  * Copyright(c) 2006-2007, Ext JS, LLC.
30596  *
30597  * Originally Released Under LGPL - original licence link has changed is not relivant.
30598  *
30599  * Fork - LGPL
30600  * <script type="text/javascript">
30601  */
30602 /**
30603  * @class Roo.QuickTips
30604  * Provides attractive and customizable tooltips for any element.
30605  * @singleton
30606  */
30607 Roo.QuickTips = function(){
30608     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30609     var ce, bd, xy, dd;
30610     var visible = false, disabled = true, inited = false;
30611     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30612     
30613     var onOver = function(e){
30614         if(disabled){
30615             return;
30616         }
30617         var t = e.getTarget();
30618         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30619             return;
30620         }
30621         if(ce && t == ce.el){
30622             clearTimeout(hideProc);
30623             return;
30624         }
30625         if(t && tagEls[t.id]){
30626             tagEls[t.id].el = t;
30627             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30628             return;
30629         }
30630         var ttp, et = Roo.fly(t);
30631         var ns = cfg.namespace;
30632         if(tm.interceptTitles && t.title){
30633             ttp = t.title;
30634             t.qtip = ttp;
30635             t.removeAttribute("title");
30636             e.preventDefault();
30637         }else{
30638             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30639         }
30640         if(ttp){
30641             showProc = show.defer(tm.showDelay, tm, [{
30642                 el: t, 
30643                 text: ttp, 
30644                 width: et.getAttributeNS(ns, cfg.width),
30645                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30646                 title: et.getAttributeNS(ns, cfg.title),
30647                     cls: et.getAttributeNS(ns, cfg.cls)
30648             }]);
30649         }
30650     };
30651     
30652     var onOut = function(e){
30653         clearTimeout(showProc);
30654         var t = e.getTarget();
30655         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30656             hideProc = setTimeout(hide, tm.hideDelay);
30657         }
30658     };
30659     
30660     var onMove = function(e){
30661         if(disabled){
30662             return;
30663         }
30664         xy = e.getXY();
30665         xy[1] += 18;
30666         if(tm.trackMouse && ce){
30667             el.setXY(xy);
30668         }
30669     };
30670     
30671     var onDown = function(e){
30672         clearTimeout(showProc);
30673         clearTimeout(hideProc);
30674         if(!e.within(el)){
30675             if(tm.hideOnClick){
30676                 hide();
30677                 tm.disable();
30678                 tm.enable.defer(100, tm);
30679             }
30680         }
30681     };
30682     
30683     var getPad = function(){
30684         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30685     };
30686
30687     var show = function(o){
30688         if(disabled){
30689             return;
30690         }
30691         clearTimeout(dismissProc);
30692         ce = o;
30693         if(removeCls){ // in case manually hidden
30694             el.removeClass(removeCls);
30695             removeCls = null;
30696         }
30697         if(ce.cls){
30698             el.addClass(ce.cls);
30699             removeCls = ce.cls;
30700         }
30701         if(ce.title){
30702             tipTitle.update(ce.title);
30703             tipTitle.show();
30704         }else{
30705             tipTitle.update('');
30706             tipTitle.hide();
30707         }
30708         el.dom.style.width  = tm.maxWidth+'px';
30709         //tipBody.dom.style.width = '';
30710         tipBodyText.update(o.text);
30711         var p = getPad(), w = ce.width;
30712         if(!w){
30713             var td = tipBodyText.dom;
30714             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30715             if(aw > tm.maxWidth){
30716                 w = tm.maxWidth;
30717             }else if(aw < tm.minWidth){
30718                 w = tm.minWidth;
30719             }else{
30720                 w = aw;
30721             }
30722         }
30723         //tipBody.setWidth(w);
30724         el.setWidth(parseInt(w, 10) + p);
30725         if(ce.autoHide === false){
30726             close.setDisplayed(true);
30727             if(dd){
30728                 dd.unlock();
30729             }
30730         }else{
30731             close.setDisplayed(false);
30732             if(dd){
30733                 dd.lock();
30734             }
30735         }
30736         if(xy){
30737             el.avoidY = xy[1]-18;
30738             el.setXY(xy);
30739         }
30740         if(tm.animate){
30741             el.setOpacity(.1);
30742             el.setStyle("visibility", "visible");
30743             el.fadeIn({callback: afterShow});
30744         }else{
30745             afterShow();
30746         }
30747     };
30748     
30749     var afterShow = function(){
30750         if(ce){
30751             el.show();
30752             esc.enable();
30753             if(tm.autoDismiss && ce.autoHide !== false){
30754                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30755             }
30756         }
30757     };
30758     
30759     var hide = function(noanim){
30760         clearTimeout(dismissProc);
30761         clearTimeout(hideProc);
30762         ce = null;
30763         if(el.isVisible()){
30764             esc.disable();
30765             if(noanim !== true && tm.animate){
30766                 el.fadeOut({callback: afterHide});
30767             }else{
30768                 afterHide();
30769             } 
30770         }
30771     };
30772     
30773     var afterHide = function(){
30774         el.hide();
30775         if(removeCls){
30776             el.removeClass(removeCls);
30777             removeCls = null;
30778         }
30779     };
30780     
30781     return {
30782         /**
30783         * @cfg {Number} minWidth
30784         * The minimum width of the quick tip (defaults to 40)
30785         */
30786        minWidth : 40,
30787         /**
30788         * @cfg {Number} maxWidth
30789         * The maximum width of the quick tip (defaults to 300)
30790         */
30791        maxWidth : 300,
30792         /**
30793         * @cfg {Boolean} interceptTitles
30794         * True to automatically use the element's DOM title value if available (defaults to false)
30795         */
30796        interceptTitles : false,
30797         /**
30798         * @cfg {Boolean} trackMouse
30799         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30800         */
30801        trackMouse : false,
30802         /**
30803         * @cfg {Boolean} hideOnClick
30804         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30805         */
30806        hideOnClick : true,
30807         /**
30808         * @cfg {Number} showDelay
30809         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30810         */
30811        showDelay : 500,
30812         /**
30813         * @cfg {Number} hideDelay
30814         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30815         */
30816        hideDelay : 200,
30817         /**
30818         * @cfg {Boolean} autoHide
30819         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30820         * Used in conjunction with hideDelay.
30821         */
30822        autoHide : true,
30823         /**
30824         * @cfg {Boolean}
30825         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30826         * (defaults to true).  Used in conjunction with autoDismissDelay.
30827         */
30828        autoDismiss : true,
30829         /**
30830         * @cfg {Number}
30831         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30832         */
30833        autoDismissDelay : 5000,
30834        /**
30835         * @cfg {Boolean} animate
30836         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30837         */
30838        animate : false,
30839
30840        /**
30841         * @cfg {String} title
30842         * Title text to display (defaults to '').  This can be any valid HTML markup.
30843         */
30844         title: '',
30845        /**
30846         * @cfg {String} text
30847         * Body text to display (defaults to '').  This can be any valid HTML markup.
30848         */
30849         text : '',
30850        /**
30851         * @cfg {String} cls
30852         * A CSS class to apply to the base quick tip element (defaults to '').
30853         */
30854         cls : '',
30855        /**
30856         * @cfg {Number} width
30857         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30858         * minWidth or maxWidth.
30859         */
30860         width : null,
30861
30862     /**
30863      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30864      * or display QuickTips in a page.
30865      */
30866        init : function(){
30867           tm = Roo.QuickTips;
30868           cfg = tm.tagConfig;
30869           if(!inited){
30870               if(!Roo.isReady){ // allow calling of init() before onReady
30871                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30872                   return;
30873               }
30874               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30875               el.fxDefaults = {stopFx: true};
30876               // maximum custom styling
30877               //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>');
30878               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>');              
30879               tipTitle = el.child('h3');
30880               tipTitle.enableDisplayMode("block");
30881               tipBody = el.child('div.x-tip-bd');
30882               tipBodyText = el.child('div.x-tip-bd-inner');
30883               //bdLeft = el.child('div.x-tip-bd-left');
30884               //bdRight = el.child('div.x-tip-bd-right');
30885               close = el.child('div.x-tip-close');
30886               close.enableDisplayMode("block");
30887               close.on("click", hide);
30888               var d = Roo.get(document);
30889               d.on("mousedown", onDown);
30890               d.on("mouseover", onOver);
30891               d.on("mouseout", onOut);
30892               d.on("mousemove", onMove);
30893               esc = d.addKeyListener(27, hide);
30894               esc.disable();
30895               if(Roo.dd.DD){
30896                   dd = el.initDD("default", null, {
30897                       onDrag : function(){
30898                           el.sync();  
30899                       }
30900                   });
30901                   dd.setHandleElId(tipTitle.id);
30902                   dd.lock();
30903               }
30904               inited = true;
30905           }
30906           this.enable(); 
30907        },
30908
30909     /**
30910      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30911      * are supported:
30912      * <pre>
30913 Property    Type                   Description
30914 ----------  ---------------------  ------------------------------------------------------------------------
30915 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30916      * </ul>
30917      * @param {Object} config The config object
30918      */
30919        register : function(config){
30920            var cs = config instanceof Array ? config : arguments;
30921            for(var i = 0, len = cs.length; i < len; i++) {
30922                var c = cs[i];
30923                var target = c.target;
30924                if(target){
30925                    if(target instanceof Array){
30926                        for(var j = 0, jlen = target.length; j < jlen; j++){
30927                            tagEls[target[j]] = c;
30928                        }
30929                    }else{
30930                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30931                    }
30932                }
30933            }
30934        },
30935
30936     /**
30937      * Removes this quick tip from its element and destroys it.
30938      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30939      */
30940        unregister : function(el){
30941            delete tagEls[Roo.id(el)];
30942        },
30943
30944     /**
30945      * Enable this quick tip.
30946      */
30947        enable : function(){
30948            if(inited && disabled){
30949                locks.pop();
30950                if(locks.length < 1){
30951                    disabled = false;
30952                }
30953            }
30954        },
30955
30956     /**
30957      * Disable this quick tip.
30958      */
30959        disable : function(){
30960           disabled = true;
30961           clearTimeout(showProc);
30962           clearTimeout(hideProc);
30963           clearTimeout(dismissProc);
30964           if(ce){
30965               hide(true);
30966           }
30967           locks.push(1);
30968        },
30969
30970     /**
30971      * Returns true if the quick tip is enabled, else false.
30972      */
30973        isEnabled : function(){
30974             return !disabled;
30975        },
30976
30977         // private
30978        tagConfig : {
30979            namespace : "ext",
30980            attribute : "qtip",
30981            width : "width",
30982            target : "target",
30983            title : "qtitle",
30984            hide : "hide",
30985            cls : "qclass"
30986        }
30987    };
30988 }();
30989
30990 // backwards compat
30991 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30992  * Based on:
30993  * Ext JS Library 1.1.1
30994  * Copyright(c) 2006-2007, Ext JS, LLC.
30995  *
30996  * Originally Released Under LGPL - original licence link has changed is not relivant.
30997  *
30998  * Fork - LGPL
30999  * <script type="text/javascript">
31000  */
31001  
31002
31003 /**
31004  * @class Roo.tree.TreePanel
31005  * @extends Roo.data.Tree
31006
31007  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31008  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31009  * @cfg {Boolean} enableDD true to enable drag and drop
31010  * @cfg {Boolean} enableDrag true to enable just drag
31011  * @cfg {Boolean} enableDrop true to enable just drop
31012  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31013  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31014  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31015  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31016  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31017  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31018  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31019  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31020  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31021  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31022  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31023  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31024  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31025  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31026  * @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>
31027  * @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>
31028  * 
31029  * @constructor
31030  * @param {String/HTMLElement/Element} el The container element
31031  * @param {Object} config
31032  */
31033 Roo.tree.TreePanel = function(el, config){
31034     var root = false;
31035     var loader = false;
31036     if (config.root) {
31037         root = config.root;
31038         delete config.root;
31039     }
31040     if (config.loader) {
31041         loader = config.loader;
31042         delete config.loader;
31043     }
31044     
31045     Roo.apply(this, config);
31046     Roo.tree.TreePanel.superclass.constructor.call(this);
31047     this.el = Roo.get(el);
31048     this.el.addClass('x-tree');
31049     //console.log(root);
31050     if (root) {
31051         this.setRootNode( Roo.factory(root, Roo.tree));
31052     }
31053     if (loader) {
31054         this.loader = Roo.factory(loader, Roo.tree);
31055     }
31056    /**
31057     * Read-only. The id of the container element becomes this TreePanel's id.
31058     */
31059     this.id = this.el.id;
31060     this.addEvents({
31061         /**
31062         * @event beforeload
31063         * Fires before a node is loaded, return false to cancel
31064         * @param {Node} node The node being loaded
31065         */
31066         "beforeload" : true,
31067         /**
31068         * @event load
31069         * Fires when a node is loaded
31070         * @param {Node} node The node that was loaded
31071         */
31072         "load" : true,
31073         /**
31074         * @event textchange
31075         * Fires when the text for a node is changed
31076         * @param {Node} node The node
31077         * @param {String} text The new text
31078         * @param {String} oldText The old text
31079         */
31080         "textchange" : true,
31081         /**
31082         * @event beforeexpand
31083         * Fires before a node is expanded, return false to cancel.
31084         * @param {Node} node The node
31085         * @param {Boolean} deep
31086         * @param {Boolean} anim
31087         */
31088         "beforeexpand" : true,
31089         /**
31090         * @event beforecollapse
31091         * Fires before a node is collapsed, return false to cancel.
31092         * @param {Node} node The node
31093         * @param {Boolean} deep
31094         * @param {Boolean} anim
31095         */
31096         "beforecollapse" : true,
31097         /**
31098         * @event expand
31099         * Fires when a node is expanded
31100         * @param {Node} node The node
31101         */
31102         "expand" : true,
31103         /**
31104         * @event disabledchange
31105         * Fires when the disabled status of a node changes
31106         * @param {Node} node The node
31107         * @param {Boolean} disabled
31108         */
31109         "disabledchange" : true,
31110         /**
31111         * @event collapse
31112         * Fires when a node is collapsed
31113         * @param {Node} node The node
31114         */
31115         "collapse" : true,
31116         /**
31117         * @event beforeclick
31118         * Fires before click processing on a node. Return false to cancel the default action.
31119         * @param {Node} node The node
31120         * @param {Roo.EventObject} e The event object
31121         */
31122         "beforeclick":true,
31123         /**
31124         * @event checkchange
31125         * Fires when a node with a checkbox's checked property changes
31126         * @param {Node} this This node
31127         * @param {Boolean} checked
31128         */
31129         "checkchange":true,
31130         /**
31131         * @event click
31132         * Fires when a node is clicked
31133         * @param {Node} node The node
31134         * @param {Roo.EventObject} e The event object
31135         */
31136         "click":true,
31137         /**
31138         * @event dblclick
31139         * Fires when a node is double clicked
31140         * @param {Node} node The node
31141         * @param {Roo.EventObject} e The event object
31142         */
31143         "dblclick":true,
31144         /**
31145         * @event contextmenu
31146         * Fires when a node is right clicked
31147         * @param {Node} node The node
31148         * @param {Roo.EventObject} e The event object
31149         */
31150         "contextmenu":true,
31151         /**
31152         * @event beforechildrenrendered
31153         * Fires right before the child nodes for a node are rendered
31154         * @param {Node} node The node
31155         */
31156         "beforechildrenrendered":true,
31157         /**
31158         * @event startdrag
31159         * Fires when a node starts being dragged
31160         * @param {Roo.tree.TreePanel} this
31161         * @param {Roo.tree.TreeNode} node
31162         * @param {event} e The raw browser event
31163         */ 
31164        "startdrag" : true,
31165        /**
31166         * @event enddrag
31167         * Fires when a drag operation is complete
31168         * @param {Roo.tree.TreePanel} this
31169         * @param {Roo.tree.TreeNode} node
31170         * @param {event} e The raw browser event
31171         */
31172        "enddrag" : true,
31173        /**
31174         * @event dragdrop
31175         * Fires when a dragged node is dropped on a valid DD target
31176         * @param {Roo.tree.TreePanel} this
31177         * @param {Roo.tree.TreeNode} node
31178         * @param {DD} dd The dd it was dropped on
31179         * @param {event} e The raw browser event
31180         */
31181        "dragdrop" : true,
31182        /**
31183         * @event beforenodedrop
31184         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31185         * passed to handlers has the following properties:<br />
31186         * <ul style="padding:5px;padding-left:16px;">
31187         * <li>tree - The TreePanel</li>
31188         * <li>target - The node being targeted for the drop</li>
31189         * <li>data - The drag data from the drag source</li>
31190         * <li>point - The point of the drop - append, above or below</li>
31191         * <li>source - The drag source</li>
31192         * <li>rawEvent - Raw mouse event</li>
31193         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31194         * to be inserted by setting them on this object.</li>
31195         * <li>cancel - Set this to true to cancel the drop.</li>
31196         * </ul>
31197         * @param {Object} dropEvent
31198         */
31199        "beforenodedrop" : true,
31200        /**
31201         * @event nodedrop
31202         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31203         * passed to handlers has the following properties:<br />
31204         * <ul style="padding:5px;padding-left:16px;">
31205         * <li>tree - The TreePanel</li>
31206         * <li>target - The node being targeted for the drop</li>
31207         * <li>data - The drag data from the drag source</li>
31208         * <li>point - The point of the drop - append, above or below</li>
31209         * <li>source - The drag source</li>
31210         * <li>rawEvent - Raw mouse event</li>
31211         * <li>dropNode - Dropped node(s).</li>
31212         * </ul>
31213         * @param {Object} dropEvent
31214         */
31215        "nodedrop" : true,
31216         /**
31217         * @event nodedragover
31218         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31219         * passed to handlers has the following properties:<br />
31220         * <ul style="padding:5px;padding-left:16px;">
31221         * <li>tree - The TreePanel</li>
31222         * <li>target - The node being targeted for the drop</li>
31223         * <li>data - The drag data from the drag source</li>
31224         * <li>point - The point of the drop - append, above or below</li>
31225         * <li>source - The drag source</li>
31226         * <li>rawEvent - Raw mouse event</li>
31227         * <li>dropNode - Drop node(s) provided by the source.</li>
31228         * <li>cancel - Set this to true to signal drop not allowed.</li>
31229         * </ul>
31230         * @param {Object} dragOverEvent
31231         */
31232        "nodedragover" : true
31233         
31234     });
31235     if(this.singleExpand){
31236        this.on("beforeexpand", this.restrictExpand, this);
31237     }
31238     if (this.editor) {
31239         this.editor.tree = this;
31240         this.editor = Roo.factory(this.editor, Roo.tree);
31241     }
31242     
31243     if (this.selModel) {
31244         this.selModel = Roo.factory(this.selModel, Roo.tree);
31245     }
31246    
31247 };
31248 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31249     rootVisible : true,
31250     animate: Roo.enableFx,
31251     lines : true,
31252     enableDD : false,
31253     hlDrop : Roo.enableFx,
31254   
31255     renderer: false,
31256     
31257     rendererTip: false,
31258     // private
31259     restrictExpand : function(node){
31260         var p = node.parentNode;
31261         if(p){
31262             if(p.expandedChild && p.expandedChild.parentNode == p){
31263                 p.expandedChild.collapse();
31264             }
31265             p.expandedChild = node;
31266         }
31267     },
31268
31269     // private override
31270     setRootNode : function(node){
31271         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31272         if(!this.rootVisible){
31273             node.ui = new Roo.tree.RootTreeNodeUI(node);
31274         }
31275         return node;
31276     },
31277
31278     /**
31279      * Returns the container element for this TreePanel
31280      */
31281     getEl : function(){
31282         return this.el;
31283     },
31284
31285     /**
31286      * Returns the default TreeLoader for this TreePanel
31287      */
31288     getLoader : function(){
31289         return this.loader;
31290     },
31291
31292     /**
31293      * Expand all nodes
31294      */
31295     expandAll : function(){
31296         this.root.expand(true);
31297     },
31298
31299     /**
31300      * Collapse all nodes
31301      */
31302     collapseAll : function(){
31303         this.root.collapse(true);
31304     },
31305
31306     /**
31307      * Returns the selection model used by this TreePanel
31308      */
31309     getSelectionModel : function(){
31310         if(!this.selModel){
31311             this.selModel = new Roo.tree.DefaultSelectionModel();
31312         }
31313         return this.selModel;
31314     },
31315
31316     /**
31317      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31318      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31319      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31320      * @return {Array}
31321      */
31322     getChecked : function(a, startNode){
31323         startNode = startNode || this.root;
31324         var r = [];
31325         var f = function(){
31326             if(this.attributes.checked){
31327                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31328             }
31329         }
31330         startNode.cascade(f);
31331         return r;
31332     },
31333
31334     /**
31335      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31336      * @param {String} path
31337      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31338      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31339      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31340      */
31341     expandPath : function(path, attr, callback){
31342         attr = attr || "id";
31343         var keys = path.split(this.pathSeparator);
31344         var curNode = this.root;
31345         if(curNode.attributes[attr] != keys[1]){ // invalid root
31346             if(callback){
31347                 callback(false, null);
31348             }
31349             return;
31350         }
31351         var index = 1;
31352         var f = function(){
31353             if(++index == keys.length){
31354                 if(callback){
31355                     callback(true, curNode);
31356                 }
31357                 return;
31358             }
31359             var c = curNode.findChild(attr, keys[index]);
31360             if(!c){
31361                 if(callback){
31362                     callback(false, curNode);
31363                 }
31364                 return;
31365             }
31366             curNode = c;
31367             c.expand(false, false, f);
31368         };
31369         curNode.expand(false, false, f);
31370     },
31371
31372     /**
31373      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31374      * @param {String} path
31375      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31376      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31377      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31378      */
31379     selectPath : function(path, attr, callback){
31380         attr = attr || "id";
31381         var keys = path.split(this.pathSeparator);
31382         var v = keys.pop();
31383         if(keys.length > 0){
31384             var f = function(success, node){
31385                 if(success && node){
31386                     var n = node.findChild(attr, v);
31387                     if(n){
31388                         n.select();
31389                         if(callback){
31390                             callback(true, n);
31391                         }
31392                     }else if(callback){
31393                         callback(false, n);
31394                     }
31395                 }else{
31396                     if(callback){
31397                         callback(false, n);
31398                     }
31399                 }
31400             };
31401             this.expandPath(keys.join(this.pathSeparator), attr, f);
31402         }else{
31403             this.root.select();
31404             if(callback){
31405                 callback(true, this.root);
31406             }
31407         }
31408     },
31409
31410     getTreeEl : function(){
31411         return this.el;
31412     },
31413
31414     /**
31415      * Trigger rendering of this TreePanel
31416      */
31417     render : function(){
31418         if (this.innerCt) {
31419             return this; // stop it rendering more than once!!
31420         }
31421         
31422         this.innerCt = this.el.createChild({tag:"ul",
31423                cls:"x-tree-root-ct " +
31424                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31425
31426         if(this.containerScroll){
31427             Roo.dd.ScrollManager.register(this.el);
31428         }
31429         if((this.enableDD || this.enableDrop) && !this.dropZone){
31430            /**
31431             * The dropZone used by this tree if drop is enabled
31432             * @type Roo.tree.TreeDropZone
31433             */
31434              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31435                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31436            });
31437         }
31438         if((this.enableDD || this.enableDrag) && !this.dragZone){
31439            /**
31440             * The dragZone used by this tree if drag is enabled
31441             * @type Roo.tree.TreeDragZone
31442             */
31443             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31444                ddGroup: this.ddGroup || "TreeDD",
31445                scroll: this.ddScroll
31446            });
31447         }
31448         this.getSelectionModel().init(this);
31449         if (!this.root) {
31450             console.log("ROOT not set in tree");
31451             return;
31452         }
31453         this.root.render();
31454         if(!this.rootVisible){
31455             this.root.renderChildren();
31456         }
31457         return this;
31458     }
31459 });/*
31460  * Based on:
31461  * Ext JS Library 1.1.1
31462  * Copyright(c) 2006-2007, Ext JS, LLC.
31463  *
31464  * Originally Released Under LGPL - original licence link has changed is not relivant.
31465  *
31466  * Fork - LGPL
31467  * <script type="text/javascript">
31468  */
31469  
31470
31471 /**
31472  * @class Roo.tree.DefaultSelectionModel
31473  * @extends Roo.util.Observable
31474  * The default single selection for a TreePanel.
31475  * @param {Object} cfg Configuration
31476  */
31477 Roo.tree.DefaultSelectionModel = function(cfg){
31478    this.selNode = null;
31479    
31480    
31481    
31482    this.addEvents({
31483        /**
31484         * @event selectionchange
31485         * Fires when the selected node changes
31486         * @param {DefaultSelectionModel} this
31487         * @param {TreeNode} node the new selection
31488         */
31489        "selectionchange" : true,
31490
31491        /**
31492         * @event beforeselect
31493         * Fires before the selected node changes, return false to cancel the change
31494         * @param {DefaultSelectionModel} this
31495         * @param {TreeNode} node the new selection
31496         * @param {TreeNode} node the old selection
31497         */
31498        "beforeselect" : true
31499    });
31500    
31501     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31502 };
31503
31504 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31505     init : function(tree){
31506         this.tree = tree;
31507         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31508         tree.on("click", this.onNodeClick, this);
31509     },
31510     
31511     onNodeClick : function(node, e){
31512         if (e.ctrlKey && this.selNode == node)  {
31513             this.unselect(node);
31514             return;
31515         }
31516         this.select(node);
31517     },
31518     
31519     /**
31520      * Select a node.
31521      * @param {TreeNode} node The node to select
31522      * @return {TreeNode} The selected node
31523      */
31524     select : function(node){
31525         var last = this.selNode;
31526         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31527             if(last){
31528                 last.ui.onSelectedChange(false);
31529             }
31530             this.selNode = node;
31531             node.ui.onSelectedChange(true);
31532             this.fireEvent("selectionchange", this, node, last);
31533         }
31534         return node;
31535     },
31536     
31537     /**
31538      * Deselect a node.
31539      * @param {TreeNode} node The node to unselect
31540      */
31541     unselect : function(node){
31542         if(this.selNode == node){
31543             this.clearSelections();
31544         }    
31545     },
31546     
31547     /**
31548      * Clear all selections
31549      */
31550     clearSelections : function(){
31551         var n = this.selNode;
31552         if(n){
31553             n.ui.onSelectedChange(false);
31554             this.selNode = null;
31555             this.fireEvent("selectionchange", this, null);
31556         }
31557         return n;
31558     },
31559     
31560     /**
31561      * Get the selected node
31562      * @return {TreeNode} The selected node
31563      */
31564     getSelectedNode : function(){
31565         return this.selNode;    
31566     },
31567     
31568     /**
31569      * Returns true if the node is selected
31570      * @param {TreeNode} node The node to check
31571      * @return {Boolean}
31572      */
31573     isSelected : function(node){
31574         return this.selNode == node;  
31575     },
31576
31577     /**
31578      * Selects the node above the selected node in the tree, intelligently walking the nodes
31579      * @return TreeNode The new selection
31580      */
31581     selectPrevious : function(){
31582         var s = this.selNode || this.lastSelNode;
31583         if(!s){
31584             return null;
31585         }
31586         var ps = s.previousSibling;
31587         if(ps){
31588             if(!ps.isExpanded() || ps.childNodes.length < 1){
31589                 return this.select(ps);
31590             } else{
31591                 var lc = ps.lastChild;
31592                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31593                     lc = lc.lastChild;
31594                 }
31595                 return this.select(lc);
31596             }
31597         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31598             return this.select(s.parentNode);
31599         }
31600         return null;
31601     },
31602
31603     /**
31604      * Selects the node above the selected node in the tree, intelligently walking the nodes
31605      * @return TreeNode The new selection
31606      */
31607     selectNext : function(){
31608         var s = this.selNode || this.lastSelNode;
31609         if(!s){
31610             return null;
31611         }
31612         if(s.firstChild && s.isExpanded()){
31613              return this.select(s.firstChild);
31614          }else if(s.nextSibling){
31615              return this.select(s.nextSibling);
31616          }else if(s.parentNode){
31617             var newS = null;
31618             s.parentNode.bubble(function(){
31619                 if(this.nextSibling){
31620                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31621                     return false;
31622                 }
31623             });
31624             return newS;
31625          }
31626         return null;
31627     },
31628
31629     onKeyDown : function(e){
31630         var s = this.selNode || this.lastSelNode;
31631         // undesirable, but required
31632         var sm = this;
31633         if(!s){
31634             return;
31635         }
31636         var k = e.getKey();
31637         switch(k){
31638              case e.DOWN:
31639                  e.stopEvent();
31640                  this.selectNext();
31641              break;
31642              case e.UP:
31643                  e.stopEvent();
31644                  this.selectPrevious();
31645              break;
31646              case e.RIGHT:
31647                  e.preventDefault();
31648                  if(s.hasChildNodes()){
31649                      if(!s.isExpanded()){
31650                          s.expand();
31651                      }else if(s.firstChild){
31652                          this.select(s.firstChild, e);
31653                      }
31654                  }
31655              break;
31656              case e.LEFT:
31657                  e.preventDefault();
31658                  if(s.hasChildNodes() && s.isExpanded()){
31659                      s.collapse();
31660                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31661                      this.select(s.parentNode, e);
31662                  }
31663              break;
31664         };
31665     }
31666 });
31667
31668 /**
31669  * @class Roo.tree.MultiSelectionModel
31670  * @extends Roo.util.Observable
31671  * Multi selection for a TreePanel.
31672  * @param {Object} cfg Configuration
31673  */
31674 Roo.tree.MultiSelectionModel = function(){
31675    this.selNodes = [];
31676    this.selMap = {};
31677    this.addEvents({
31678        /**
31679         * @event selectionchange
31680         * Fires when the selected nodes change
31681         * @param {MultiSelectionModel} this
31682         * @param {Array} nodes Array of the selected nodes
31683         */
31684        "selectionchange" : true
31685    });
31686    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31687    
31688 };
31689
31690 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31691     init : function(tree){
31692         this.tree = tree;
31693         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31694         tree.on("click", this.onNodeClick, this);
31695     },
31696     
31697     onNodeClick : function(node, e){
31698         this.select(node, e, e.ctrlKey);
31699     },
31700     
31701     /**
31702      * Select a node.
31703      * @param {TreeNode} node The node to select
31704      * @param {EventObject} e (optional) An event associated with the selection
31705      * @param {Boolean} keepExisting True to retain existing selections
31706      * @return {TreeNode} The selected node
31707      */
31708     select : function(node, e, keepExisting){
31709         if(keepExisting !== true){
31710             this.clearSelections(true);
31711         }
31712         if(this.isSelected(node)){
31713             this.lastSelNode = node;
31714             return node;
31715         }
31716         this.selNodes.push(node);
31717         this.selMap[node.id] = node;
31718         this.lastSelNode = node;
31719         node.ui.onSelectedChange(true);
31720         this.fireEvent("selectionchange", this, this.selNodes);
31721         return node;
31722     },
31723     
31724     /**
31725      * Deselect a node.
31726      * @param {TreeNode} node The node to unselect
31727      */
31728     unselect : function(node){
31729         if(this.selMap[node.id]){
31730             node.ui.onSelectedChange(false);
31731             var sn = this.selNodes;
31732             var index = -1;
31733             if(sn.indexOf){
31734                 index = sn.indexOf(node);
31735             }else{
31736                 for(var i = 0, len = sn.length; i < len; i++){
31737                     if(sn[i] == node){
31738                         index = i;
31739                         break;
31740                     }
31741                 }
31742             }
31743             if(index != -1){
31744                 this.selNodes.splice(index, 1);
31745             }
31746             delete this.selMap[node.id];
31747             this.fireEvent("selectionchange", this, this.selNodes);
31748         }
31749     },
31750     
31751     /**
31752      * Clear all selections
31753      */
31754     clearSelections : function(suppressEvent){
31755         var sn = this.selNodes;
31756         if(sn.length > 0){
31757             for(var i = 0, len = sn.length; i < len; i++){
31758                 sn[i].ui.onSelectedChange(false);
31759             }
31760             this.selNodes = [];
31761             this.selMap = {};
31762             if(suppressEvent !== true){
31763                 this.fireEvent("selectionchange", this, this.selNodes);
31764             }
31765         }
31766     },
31767     
31768     /**
31769      * Returns true if the node is selected
31770      * @param {TreeNode} node The node to check
31771      * @return {Boolean}
31772      */
31773     isSelected : function(node){
31774         return this.selMap[node.id] ? true : false;  
31775     },
31776     
31777     /**
31778      * Returns an array of the selected nodes
31779      * @return {Array}
31780      */
31781     getSelectedNodes : function(){
31782         return this.selNodes;    
31783     },
31784
31785     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31786
31787     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31788
31789     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31790 });/*
31791  * Based on:
31792  * Ext JS Library 1.1.1
31793  * Copyright(c) 2006-2007, Ext JS, LLC.
31794  *
31795  * Originally Released Under LGPL - original licence link has changed is not relivant.
31796  *
31797  * Fork - LGPL
31798  * <script type="text/javascript">
31799  */
31800  
31801 /**
31802  * @class Roo.tree.TreeNode
31803  * @extends Roo.data.Node
31804  * @cfg {String} text The text for this node
31805  * @cfg {Boolean} expanded true to start the node expanded
31806  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31807  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31808  * @cfg {Boolean} disabled true to start the node disabled
31809  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31810  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31811  * @cfg {String} cls A css class to be added to the node
31812  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31813  * @cfg {String} href URL of the link used for the node (defaults to #)
31814  * @cfg {String} hrefTarget target frame for the link
31815  * @cfg {String} qtip An Ext QuickTip for the node
31816  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31817  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31818  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31819  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31820  * (defaults to undefined with no checkbox rendered)
31821  * @constructor
31822  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31823  */
31824 Roo.tree.TreeNode = function(attributes){
31825     attributes = attributes || {};
31826     if(typeof attributes == "string"){
31827         attributes = {text: attributes};
31828     }
31829     this.childrenRendered = false;
31830     this.rendered = false;
31831     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31832     this.expanded = attributes.expanded === true;
31833     this.isTarget = attributes.isTarget !== false;
31834     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31835     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31836
31837     /**
31838      * Read-only. The text for this node. To change it use setText().
31839      * @type String
31840      */
31841     this.text = attributes.text;
31842     /**
31843      * True if this node is disabled.
31844      * @type Boolean
31845      */
31846     this.disabled = attributes.disabled === true;
31847
31848     this.addEvents({
31849         /**
31850         * @event textchange
31851         * Fires when the text for this node is changed
31852         * @param {Node} this This node
31853         * @param {String} text The new text
31854         * @param {String} oldText The old text
31855         */
31856         "textchange" : true,
31857         /**
31858         * @event beforeexpand
31859         * Fires before this node is expanded, return false to cancel.
31860         * @param {Node} this This node
31861         * @param {Boolean} deep
31862         * @param {Boolean} anim
31863         */
31864         "beforeexpand" : true,
31865         /**
31866         * @event beforecollapse
31867         * Fires before this node is collapsed, return false to cancel.
31868         * @param {Node} this This node
31869         * @param {Boolean} deep
31870         * @param {Boolean} anim
31871         */
31872         "beforecollapse" : true,
31873         /**
31874         * @event expand
31875         * Fires when this node is expanded
31876         * @param {Node} this This node
31877         */
31878         "expand" : true,
31879         /**
31880         * @event disabledchange
31881         * Fires when the disabled status of this node changes
31882         * @param {Node} this This node
31883         * @param {Boolean} disabled
31884         */
31885         "disabledchange" : true,
31886         /**
31887         * @event collapse
31888         * Fires when this node is collapsed
31889         * @param {Node} this This node
31890         */
31891         "collapse" : true,
31892         /**
31893         * @event beforeclick
31894         * Fires before click processing. Return false to cancel the default action.
31895         * @param {Node} this This node
31896         * @param {Roo.EventObject} e The event object
31897         */
31898         "beforeclick":true,
31899         /**
31900         * @event checkchange
31901         * Fires when a node with a checkbox's checked property changes
31902         * @param {Node} this This node
31903         * @param {Boolean} checked
31904         */
31905         "checkchange":true,
31906         /**
31907         * @event click
31908         * Fires when this node is clicked
31909         * @param {Node} this This node
31910         * @param {Roo.EventObject} e The event object
31911         */
31912         "click":true,
31913         /**
31914         * @event dblclick
31915         * Fires when this node is double clicked
31916         * @param {Node} this This node
31917         * @param {Roo.EventObject} e The event object
31918         */
31919         "dblclick":true,
31920         /**
31921         * @event contextmenu
31922         * Fires when this node is right clicked
31923         * @param {Node} this This node
31924         * @param {Roo.EventObject} e The event object
31925         */
31926         "contextmenu":true,
31927         /**
31928         * @event beforechildrenrendered
31929         * Fires right before the child nodes for this node are rendered
31930         * @param {Node} this This node
31931         */
31932         "beforechildrenrendered":true
31933     });
31934
31935     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31936
31937     /**
31938      * Read-only. The UI for this node
31939      * @type TreeNodeUI
31940      */
31941     this.ui = new uiClass(this);
31942     
31943     // finally support items[]
31944     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31945         return;
31946     }
31947     
31948     
31949     Roo.each(this.attributes.items, function(c) {
31950         this.appendChild(Roo.factory(c,Roo.Tree));
31951     }, this);
31952     delete this.attributes.items;
31953     
31954     
31955     
31956 };
31957 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31958     preventHScroll: true,
31959     /**
31960      * Returns true if this node is expanded
31961      * @return {Boolean}
31962      */
31963     isExpanded : function(){
31964         return this.expanded;
31965     },
31966
31967     /**
31968      * Returns the UI object for this node
31969      * @return {TreeNodeUI}
31970      */
31971     getUI : function(){
31972         return this.ui;
31973     },
31974
31975     // private override
31976     setFirstChild : function(node){
31977         var of = this.firstChild;
31978         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31979         if(this.childrenRendered && of && node != of){
31980             of.renderIndent(true, true);
31981         }
31982         if(this.rendered){
31983             this.renderIndent(true, true);
31984         }
31985     },
31986
31987     // private override
31988     setLastChild : function(node){
31989         var ol = this.lastChild;
31990         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31991         if(this.childrenRendered && ol && node != ol){
31992             ol.renderIndent(true, true);
31993         }
31994         if(this.rendered){
31995             this.renderIndent(true, true);
31996         }
31997     },
31998
31999     // these methods are overridden to provide lazy rendering support
32000     // private override
32001     appendChild : function()
32002     {
32003         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32004         if(node && this.childrenRendered){
32005             node.render();
32006         }
32007         this.ui.updateExpandIcon();
32008         return node;
32009     },
32010
32011     // private override
32012     removeChild : function(node){
32013         this.ownerTree.getSelectionModel().unselect(node);
32014         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32015         // if it's been rendered remove dom node
32016         if(this.childrenRendered){
32017             node.ui.remove();
32018         }
32019         if(this.childNodes.length < 1){
32020             this.collapse(false, false);
32021         }else{
32022             this.ui.updateExpandIcon();
32023         }
32024         if(!this.firstChild) {
32025             this.childrenRendered = false;
32026         }
32027         return node;
32028     },
32029
32030     // private override
32031     insertBefore : function(node, refNode){
32032         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32033         if(newNode && refNode && this.childrenRendered){
32034             node.render();
32035         }
32036         this.ui.updateExpandIcon();
32037         return newNode;
32038     },
32039
32040     /**
32041      * Sets the text for this node
32042      * @param {String} text
32043      */
32044     setText : function(text){
32045         var oldText = this.text;
32046         this.text = text;
32047         this.attributes.text = text;
32048         if(this.rendered){ // event without subscribing
32049             this.ui.onTextChange(this, text, oldText);
32050         }
32051         this.fireEvent("textchange", this, text, oldText);
32052     },
32053
32054     /**
32055      * Triggers selection of this node
32056      */
32057     select : function(){
32058         this.getOwnerTree().getSelectionModel().select(this);
32059     },
32060
32061     /**
32062      * Triggers deselection of this node
32063      */
32064     unselect : function(){
32065         this.getOwnerTree().getSelectionModel().unselect(this);
32066     },
32067
32068     /**
32069      * Returns true if this node is selected
32070      * @return {Boolean}
32071      */
32072     isSelected : function(){
32073         return this.getOwnerTree().getSelectionModel().isSelected(this);
32074     },
32075
32076     /**
32077      * Expand this node.
32078      * @param {Boolean} deep (optional) True to expand all children as well
32079      * @param {Boolean} anim (optional) false to cancel the default animation
32080      * @param {Function} callback (optional) A callback to be called when
32081      * expanding this node completes (does not wait for deep expand to complete).
32082      * Called with 1 parameter, this node.
32083      */
32084     expand : function(deep, anim, callback){
32085         if(!this.expanded){
32086             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32087                 return;
32088             }
32089             if(!this.childrenRendered){
32090                 this.renderChildren();
32091             }
32092             this.expanded = true;
32093             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32094                 this.ui.animExpand(function(){
32095                     this.fireEvent("expand", this);
32096                     if(typeof callback == "function"){
32097                         callback(this);
32098                     }
32099                     if(deep === true){
32100                         this.expandChildNodes(true);
32101                     }
32102                 }.createDelegate(this));
32103                 return;
32104             }else{
32105                 this.ui.expand();
32106                 this.fireEvent("expand", this);
32107                 if(typeof callback == "function"){
32108                     callback(this);
32109                 }
32110             }
32111         }else{
32112            if(typeof callback == "function"){
32113                callback(this);
32114            }
32115         }
32116         if(deep === true){
32117             this.expandChildNodes(true);
32118         }
32119     },
32120
32121     isHiddenRoot : function(){
32122         return this.isRoot && !this.getOwnerTree().rootVisible;
32123     },
32124
32125     /**
32126      * Collapse this node.
32127      * @param {Boolean} deep (optional) True to collapse all children as well
32128      * @param {Boolean} anim (optional) false to cancel the default animation
32129      */
32130     collapse : function(deep, anim){
32131         if(this.expanded && !this.isHiddenRoot()){
32132             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32133                 return;
32134             }
32135             this.expanded = false;
32136             if((this.getOwnerTree().animate && anim !== false) || anim){
32137                 this.ui.animCollapse(function(){
32138                     this.fireEvent("collapse", this);
32139                     if(deep === true){
32140                         this.collapseChildNodes(true);
32141                     }
32142                 }.createDelegate(this));
32143                 return;
32144             }else{
32145                 this.ui.collapse();
32146                 this.fireEvent("collapse", this);
32147             }
32148         }
32149         if(deep === true){
32150             var cs = this.childNodes;
32151             for(var i = 0, len = cs.length; i < len; i++) {
32152                 cs[i].collapse(true, false);
32153             }
32154         }
32155     },
32156
32157     // private
32158     delayedExpand : function(delay){
32159         if(!this.expandProcId){
32160             this.expandProcId = this.expand.defer(delay, this);
32161         }
32162     },
32163
32164     // private
32165     cancelExpand : function(){
32166         if(this.expandProcId){
32167             clearTimeout(this.expandProcId);
32168         }
32169         this.expandProcId = false;
32170     },
32171
32172     /**
32173      * Toggles expanded/collapsed state of the node
32174      */
32175     toggle : function(){
32176         if(this.expanded){
32177             this.collapse();
32178         }else{
32179             this.expand();
32180         }
32181     },
32182
32183     /**
32184      * Ensures all parent nodes are expanded
32185      */
32186     ensureVisible : function(callback){
32187         var tree = this.getOwnerTree();
32188         tree.expandPath(this.parentNode.getPath(), false, function(){
32189             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32190             Roo.callback(callback);
32191         }.createDelegate(this));
32192     },
32193
32194     /**
32195      * Expand all child nodes
32196      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32197      */
32198     expandChildNodes : function(deep){
32199         var cs = this.childNodes;
32200         for(var i = 0, len = cs.length; i < len; i++) {
32201                 cs[i].expand(deep);
32202         }
32203     },
32204
32205     /**
32206      * Collapse all child nodes
32207      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32208      */
32209     collapseChildNodes : function(deep){
32210         var cs = this.childNodes;
32211         for(var i = 0, len = cs.length; i < len; i++) {
32212                 cs[i].collapse(deep);
32213         }
32214     },
32215
32216     /**
32217      * Disables this node
32218      */
32219     disable : function(){
32220         this.disabled = true;
32221         this.unselect();
32222         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32223             this.ui.onDisableChange(this, true);
32224         }
32225         this.fireEvent("disabledchange", this, true);
32226     },
32227
32228     /**
32229      * Enables this node
32230      */
32231     enable : function(){
32232         this.disabled = false;
32233         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32234             this.ui.onDisableChange(this, false);
32235         }
32236         this.fireEvent("disabledchange", this, false);
32237     },
32238
32239     // private
32240     renderChildren : function(suppressEvent){
32241         if(suppressEvent !== false){
32242             this.fireEvent("beforechildrenrendered", this);
32243         }
32244         var cs = this.childNodes;
32245         for(var i = 0, len = cs.length; i < len; i++){
32246             cs[i].render(true);
32247         }
32248         this.childrenRendered = true;
32249     },
32250
32251     // private
32252     sort : function(fn, scope){
32253         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32254         if(this.childrenRendered){
32255             var cs = this.childNodes;
32256             for(var i = 0, len = cs.length; i < len; i++){
32257                 cs[i].render(true);
32258             }
32259         }
32260     },
32261
32262     // private
32263     render : function(bulkRender){
32264         this.ui.render(bulkRender);
32265         if(!this.rendered){
32266             this.rendered = true;
32267             if(this.expanded){
32268                 this.expanded = false;
32269                 this.expand(false, false);
32270             }
32271         }
32272     },
32273
32274     // private
32275     renderIndent : function(deep, refresh){
32276         if(refresh){
32277             this.ui.childIndent = null;
32278         }
32279         this.ui.renderIndent();
32280         if(deep === true && this.childrenRendered){
32281             var cs = this.childNodes;
32282             for(var i = 0, len = cs.length; i < len; i++){
32283                 cs[i].renderIndent(true, refresh);
32284             }
32285         }
32286     }
32287 });/*
32288  * Based on:
32289  * Ext JS Library 1.1.1
32290  * Copyright(c) 2006-2007, Ext JS, LLC.
32291  *
32292  * Originally Released Under LGPL - original licence link has changed is not relivant.
32293  *
32294  * Fork - LGPL
32295  * <script type="text/javascript">
32296  */
32297  
32298 /**
32299  * @class Roo.tree.AsyncTreeNode
32300  * @extends Roo.tree.TreeNode
32301  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32302  * @constructor
32303  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32304  */
32305  Roo.tree.AsyncTreeNode = function(config){
32306     this.loaded = false;
32307     this.loading = false;
32308     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32309     /**
32310     * @event beforeload
32311     * Fires before this node is loaded, return false to cancel
32312     * @param {Node} this This node
32313     */
32314     this.addEvents({'beforeload':true, 'load': true});
32315     /**
32316     * @event load
32317     * Fires when this node is loaded
32318     * @param {Node} this This node
32319     */
32320     /**
32321      * The loader used by this node (defaults to using the tree's defined loader)
32322      * @type TreeLoader
32323      * @property loader
32324      */
32325 };
32326 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32327     expand : function(deep, anim, callback){
32328         if(this.loading){ // if an async load is already running, waiting til it's done
32329             var timer;
32330             var f = function(){
32331                 if(!this.loading){ // done loading
32332                     clearInterval(timer);
32333                     this.expand(deep, anim, callback);
32334                 }
32335             }.createDelegate(this);
32336             timer = setInterval(f, 200);
32337             return;
32338         }
32339         if(!this.loaded){
32340             if(this.fireEvent("beforeload", this) === false){
32341                 return;
32342             }
32343             this.loading = true;
32344             this.ui.beforeLoad(this);
32345             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32346             if(loader){
32347                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32348                 return;
32349             }
32350         }
32351         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32352     },
32353     
32354     /**
32355      * Returns true if this node is currently loading
32356      * @return {Boolean}
32357      */
32358     isLoading : function(){
32359         return this.loading;  
32360     },
32361     
32362     loadComplete : function(deep, anim, callback){
32363         this.loading = false;
32364         this.loaded = true;
32365         this.ui.afterLoad(this);
32366         this.fireEvent("load", this);
32367         this.expand(deep, anim, callback);
32368     },
32369     
32370     /**
32371      * Returns true if this node has been loaded
32372      * @return {Boolean}
32373      */
32374     isLoaded : function(){
32375         return this.loaded;
32376     },
32377     
32378     hasChildNodes : function(){
32379         if(!this.isLeaf() && !this.loaded){
32380             return true;
32381         }else{
32382             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32383         }
32384     },
32385
32386     /**
32387      * Trigger a reload for this node
32388      * @param {Function} callback
32389      */
32390     reload : function(callback){
32391         this.collapse(false, false);
32392         while(this.firstChild){
32393             this.removeChild(this.firstChild);
32394         }
32395         this.childrenRendered = false;
32396         this.loaded = false;
32397         if(this.isHiddenRoot()){
32398             this.expanded = false;
32399         }
32400         this.expand(false, false, callback);
32401     }
32402 });/*
32403  * Based on:
32404  * Ext JS Library 1.1.1
32405  * Copyright(c) 2006-2007, Ext JS, LLC.
32406  *
32407  * Originally Released Under LGPL - original licence link has changed is not relivant.
32408  *
32409  * Fork - LGPL
32410  * <script type="text/javascript">
32411  */
32412  
32413 /**
32414  * @class Roo.tree.TreeNodeUI
32415  * @constructor
32416  * @param {Object} node The node to render
32417  * The TreeNode UI implementation is separate from the
32418  * tree implementation. Unless you are customizing the tree UI,
32419  * you should never have to use this directly.
32420  */
32421 Roo.tree.TreeNodeUI = function(node){
32422     this.node = node;
32423     this.rendered = false;
32424     this.animating = false;
32425     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32426 };
32427
32428 Roo.tree.TreeNodeUI.prototype = {
32429     removeChild : function(node){
32430         if(this.rendered){
32431             this.ctNode.removeChild(node.ui.getEl());
32432         }
32433     },
32434
32435     beforeLoad : function(){
32436          this.addClass("x-tree-node-loading");
32437     },
32438
32439     afterLoad : function(){
32440          this.removeClass("x-tree-node-loading");
32441     },
32442
32443     onTextChange : function(node, text, oldText){
32444         if(this.rendered){
32445             this.textNode.innerHTML = text;
32446         }
32447     },
32448
32449     onDisableChange : function(node, state){
32450         this.disabled = state;
32451         if(state){
32452             this.addClass("x-tree-node-disabled");
32453         }else{
32454             this.removeClass("x-tree-node-disabled");
32455         }
32456     },
32457
32458     onSelectedChange : function(state){
32459         if(state){
32460             this.focus();
32461             this.addClass("x-tree-selected");
32462         }else{
32463             //this.blur();
32464             this.removeClass("x-tree-selected");
32465         }
32466     },
32467
32468     onMove : function(tree, node, oldParent, newParent, index, refNode){
32469         this.childIndent = null;
32470         if(this.rendered){
32471             var targetNode = newParent.ui.getContainer();
32472             if(!targetNode){//target not rendered
32473                 this.holder = document.createElement("div");
32474                 this.holder.appendChild(this.wrap);
32475                 return;
32476             }
32477             var insertBefore = refNode ? refNode.ui.getEl() : null;
32478             if(insertBefore){
32479                 targetNode.insertBefore(this.wrap, insertBefore);
32480             }else{
32481                 targetNode.appendChild(this.wrap);
32482             }
32483             this.node.renderIndent(true);
32484         }
32485     },
32486
32487     addClass : function(cls){
32488         if(this.elNode){
32489             Roo.fly(this.elNode).addClass(cls);
32490         }
32491     },
32492
32493     removeClass : function(cls){
32494         if(this.elNode){
32495             Roo.fly(this.elNode).removeClass(cls);
32496         }
32497     },
32498
32499     remove : function(){
32500         if(this.rendered){
32501             this.holder = document.createElement("div");
32502             this.holder.appendChild(this.wrap);
32503         }
32504     },
32505
32506     fireEvent : function(){
32507         return this.node.fireEvent.apply(this.node, arguments);
32508     },
32509
32510     initEvents : function(){
32511         this.node.on("move", this.onMove, this);
32512         var E = Roo.EventManager;
32513         var a = this.anchor;
32514
32515         var el = Roo.fly(a, '_treeui');
32516
32517         if(Roo.isOpera){ // opera render bug ignores the CSS
32518             el.setStyle("text-decoration", "none");
32519         }
32520
32521         el.on("click", this.onClick, this);
32522         el.on("dblclick", this.onDblClick, this);
32523
32524         if(this.checkbox){
32525             Roo.EventManager.on(this.checkbox,
32526                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32527         }
32528
32529         el.on("contextmenu", this.onContextMenu, this);
32530
32531         var icon = Roo.fly(this.iconNode);
32532         icon.on("click", this.onClick, this);
32533         icon.on("dblclick", this.onDblClick, this);
32534         icon.on("contextmenu", this.onContextMenu, this);
32535         E.on(this.ecNode, "click", this.ecClick, this, true);
32536
32537         if(this.node.disabled){
32538             this.addClass("x-tree-node-disabled");
32539         }
32540         if(this.node.hidden){
32541             this.addClass("x-tree-node-disabled");
32542         }
32543         var ot = this.node.getOwnerTree();
32544         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32545         if(dd && (!this.node.isRoot || ot.rootVisible)){
32546             Roo.dd.Registry.register(this.elNode, {
32547                 node: this.node,
32548                 handles: this.getDDHandles(),
32549                 isHandle: false
32550             });
32551         }
32552     },
32553
32554     getDDHandles : function(){
32555         return [this.iconNode, this.textNode];
32556     },
32557
32558     hide : function(){
32559         if(this.rendered){
32560             this.wrap.style.display = "none";
32561         }
32562     },
32563
32564     show : function(){
32565         if(this.rendered){
32566             this.wrap.style.display = "";
32567         }
32568     },
32569
32570     onContextMenu : function(e){
32571         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32572             e.preventDefault();
32573             this.focus();
32574             this.fireEvent("contextmenu", this.node, e);
32575         }
32576     },
32577
32578     onClick : function(e){
32579         if(this.dropping){
32580             e.stopEvent();
32581             return;
32582         }
32583         if(this.fireEvent("beforeclick", this.node, e) !== false){
32584             if(!this.disabled && this.node.attributes.href){
32585                 this.fireEvent("click", this.node, e);
32586                 return;
32587             }
32588             e.preventDefault();
32589             if(this.disabled){
32590                 return;
32591             }
32592
32593             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32594                 this.node.toggle();
32595             }
32596
32597             this.fireEvent("click", this.node, e);
32598         }else{
32599             e.stopEvent();
32600         }
32601     },
32602
32603     onDblClick : function(e){
32604         e.preventDefault();
32605         if(this.disabled){
32606             return;
32607         }
32608         if(this.checkbox){
32609             this.toggleCheck();
32610         }
32611         if(!this.animating && this.node.hasChildNodes()){
32612             this.node.toggle();
32613         }
32614         this.fireEvent("dblclick", this.node, e);
32615     },
32616
32617     onCheckChange : function(){
32618         var checked = this.checkbox.checked;
32619         this.node.attributes.checked = checked;
32620         this.fireEvent('checkchange', this.node, checked);
32621     },
32622
32623     ecClick : function(e){
32624         if(!this.animating && this.node.hasChildNodes()){
32625             this.node.toggle();
32626         }
32627     },
32628
32629     startDrop : function(){
32630         this.dropping = true;
32631     },
32632
32633     // delayed drop so the click event doesn't get fired on a drop
32634     endDrop : function(){
32635        setTimeout(function(){
32636            this.dropping = false;
32637        }.createDelegate(this), 50);
32638     },
32639
32640     expand : function(){
32641         this.updateExpandIcon();
32642         this.ctNode.style.display = "";
32643     },
32644
32645     focus : function(){
32646         if(!this.node.preventHScroll){
32647             try{this.anchor.focus();
32648             }catch(e){}
32649         }else if(!Roo.isIE){
32650             try{
32651                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32652                 var l = noscroll.scrollLeft;
32653                 this.anchor.focus();
32654                 noscroll.scrollLeft = l;
32655             }catch(e){}
32656         }
32657     },
32658
32659     toggleCheck : function(value){
32660         var cb = this.checkbox;
32661         if(cb){
32662             cb.checked = (value === undefined ? !cb.checked : value);
32663         }
32664     },
32665
32666     blur : function(){
32667         try{
32668             this.anchor.blur();
32669         }catch(e){}
32670     },
32671
32672     animExpand : function(callback){
32673         var ct = Roo.get(this.ctNode);
32674         ct.stopFx();
32675         if(!this.node.hasChildNodes()){
32676             this.updateExpandIcon();
32677             this.ctNode.style.display = "";
32678             Roo.callback(callback);
32679             return;
32680         }
32681         this.animating = true;
32682         this.updateExpandIcon();
32683
32684         ct.slideIn('t', {
32685            callback : function(){
32686                this.animating = false;
32687                Roo.callback(callback);
32688             },
32689             scope: this,
32690             duration: this.node.ownerTree.duration || .25
32691         });
32692     },
32693
32694     highlight : function(){
32695         var tree = this.node.getOwnerTree();
32696         Roo.fly(this.wrap).highlight(
32697             tree.hlColor || "C3DAF9",
32698             {endColor: tree.hlBaseColor}
32699         );
32700     },
32701
32702     collapse : function(){
32703         this.updateExpandIcon();
32704         this.ctNode.style.display = "none";
32705     },
32706
32707     animCollapse : function(callback){
32708         var ct = Roo.get(this.ctNode);
32709         ct.enableDisplayMode('block');
32710         ct.stopFx();
32711
32712         this.animating = true;
32713         this.updateExpandIcon();
32714
32715         ct.slideOut('t', {
32716             callback : function(){
32717                this.animating = false;
32718                Roo.callback(callback);
32719             },
32720             scope: this,
32721             duration: this.node.ownerTree.duration || .25
32722         });
32723     },
32724
32725     getContainer : function(){
32726         return this.ctNode;
32727     },
32728
32729     getEl : function(){
32730         return this.wrap;
32731     },
32732
32733     appendDDGhost : function(ghostNode){
32734         ghostNode.appendChild(this.elNode.cloneNode(true));
32735     },
32736
32737     getDDRepairXY : function(){
32738         return Roo.lib.Dom.getXY(this.iconNode);
32739     },
32740
32741     onRender : function(){
32742         this.render();
32743     },
32744
32745     render : function(bulkRender){
32746         var n = this.node, a = n.attributes;
32747         var targetNode = n.parentNode ?
32748               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32749
32750         if(!this.rendered){
32751             this.rendered = true;
32752
32753             this.renderElements(n, a, targetNode, bulkRender);
32754
32755             if(a.qtip){
32756                if(this.textNode.setAttributeNS){
32757                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32758                    if(a.qtipTitle){
32759                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32760                    }
32761                }else{
32762                    this.textNode.setAttribute("ext:qtip", a.qtip);
32763                    if(a.qtipTitle){
32764                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32765                    }
32766                }
32767             }else if(a.qtipCfg){
32768                 a.qtipCfg.target = Roo.id(this.textNode);
32769                 Roo.QuickTips.register(a.qtipCfg);
32770             }
32771             this.initEvents();
32772             if(!this.node.expanded){
32773                 this.updateExpandIcon();
32774             }
32775         }else{
32776             if(bulkRender === true) {
32777                 targetNode.appendChild(this.wrap);
32778             }
32779         }
32780     },
32781
32782     renderElements : function(n, a, targetNode, bulkRender)
32783     {
32784         // add some indent caching, this helps performance when rendering a large tree
32785         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32786         var t = n.getOwnerTree();
32787         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32788         if (typeof(n.attributes.html) != 'undefined') {
32789             txt = n.attributes.html;
32790         }
32791         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32792         var cb = typeof a.checked == 'boolean';
32793         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32794         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32795             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32796             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32797             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32798             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32799             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32800              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32801                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32802             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32803             "</li>"];
32804
32805         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32806             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32807                                 n.nextSibling.ui.getEl(), buf.join(""));
32808         }else{
32809             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32810         }
32811
32812         this.elNode = this.wrap.childNodes[0];
32813         this.ctNode = this.wrap.childNodes[1];
32814         var cs = this.elNode.childNodes;
32815         this.indentNode = cs[0];
32816         this.ecNode = cs[1];
32817         this.iconNode = cs[2];
32818         var index = 3;
32819         if(cb){
32820             this.checkbox = cs[3];
32821             index++;
32822         }
32823         this.anchor = cs[index];
32824         this.textNode = cs[index].firstChild;
32825     },
32826
32827     getAnchor : function(){
32828         return this.anchor;
32829     },
32830
32831     getTextEl : function(){
32832         return this.textNode;
32833     },
32834
32835     getIconEl : function(){
32836         return this.iconNode;
32837     },
32838
32839     isChecked : function(){
32840         return this.checkbox ? this.checkbox.checked : false;
32841     },
32842
32843     updateExpandIcon : function(){
32844         if(this.rendered){
32845             var n = this.node, c1, c2;
32846             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32847             var hasChild = n.hasChildNodes();
32848             if(hasChild){
32849                 if(n.expanded){
32850                     cls += "-minus";
32851                     c1 = "x-tree-node-collapsed";
32852                     c2 = "x-tree-node-expanded";
32853                 }else{
32854                     cls += "-plus";
32855                     c1 = "x-tree-node-expanded";
32856                     c2 = "x-tree-node-collapsed";
32857                 }
32858                 if(this.wasLeaf){
32859                     this.removeClass("x-tree-node-leaf");
32860                     this.wasLeaf = false;
32861                 }
32862                 if(this.c1 != c1 || this.c2 != c2){
32863                     Roo.fly(this.elNode).replaceClass(c1, c2);
32864                     this.c1 = c1; this.c2 = c2;
32865                 }
32866             }else{
32867                 // this changes non-leafs into leafs if they have no children.
32868                 // it's not very rational behaviour..
32869                 
32870                 if(!this.wasLeaf && this.node.leaf){
32871                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32872                     delete this.c1;
32873                     delete this.c2;
32874                     this.wasLeaf = true;
32875                 }
32876             }
32877             var ecc = "x-tree-ec-icon "+cls;
32878             if(this.ecc != ecc){
32879                 this.ecNode.className = ecc;
32880                 this.ecc = ecc;
32881             }
32882         }
32883     },
32884
32885     getChildIndent : function(){
32886         if(!this.childIndent){
32887             var buf = [];
32888             var p = this.node;
32889             while(p){
32890                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32891                     if(!p.isLast()) {
32892                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32893                     } else {
32894                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32895                     }
32896                 }
32897                 p = p.parentNode;
32898             }
32899             this.childIndent = buf.join("");
32900         }
32901         return this.childIndent;
32902     },
32903
32904     renderIndent : function(){
32905         if(this.rendered){
32906             var indent = "";
32907             var p = this.node.parentNode;
32908             if(p){
32909                 indent = p.ui.getChildIndent();
32910             }
32911             if(this.indentMarkup != indent){ // don't rerender if not required
32912                 this.indentNode.innerHTML = indent;
32913                 this.indentMarkup = indent;
32914             }
32915             this.updateExpandIcon();
32916         }
32917     }
32918 };
32919
32920 Roo.tree.RootTreeNodeUI = function(){
32921     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32922 };
32923 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32924     render : function(){
32925         if(!this.rendered){
32926             var targetNode = this.node.ownerTree.innerCt.dom;
32927             this.node.expanded = true;
32928             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32929             this.wrap = this.ctNode = targetNode.firstChild;
32930         }
32931     },
32932     collapse : function(){
32933     },
32934     expand : function(){
32935     }
32936 });/*
32937  * Based on:
32938  * Ext JS Library 1.1.1
32939  * Copyright(c) 2006-2007, Ext JS, LLC.
32940  *
32941  * Originally Released Under LGPL - original licence link has changed is not relivant.
32942  *
32943  * Fork - LGPL
32944  * <script type="text/javascript">
32945  */
32946 /**
32947  * @class Roo.tree.TreeLoader
32948  * @extends Roo.util.Observable
32949  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32950  * nodes from a specified URL. The response must be a javascript Array definition
32951  * who's elements are node definition objects. eg:
32952  * <pre><code>
32953 {  success : true,
32954    data :      [
32955    
32956     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
32957     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
32958     ]
32959 }
32960
32961
32962 </code></pre>
32963  * <br><br>
32964  * The old style respose with just an array is still supported, but not recommended.
32965  * <br><br>
32966  *
32967  * A server request is sent, and child nodes are loaded only when a node is expanded.
32968  * The loading node's id is passed to the server under the parameter name "node" to
32969  * enable the server to produce the correct child nodes.
32970  * <br><br>
32971  * To pass extra parameters, an event handler may be attached to the "beforeload"
32972  * event, and the parameters specified in the TreeLoader's baseParams property:
32973  * <pre><code>
32974     myTreeLoader.on("beforeload", function(treeLoader, node) {
32975         this.baseParams.category = node.attributes.category;
32976     }, this);
32977 </code></pre><
32978  * This would pass an HTTP parameter called "category" to the server containing
32979  * the value of the Node's "category" attribute.
32980  * @constructor
32981  * Creates a new Treeloader.
32982  * @param {Object} config A config object containing config properties.
32983  */
32984 Roo.tree.TreeLoader = function(config){
32985     this.baseParams = {};
32986     this.requestMethod = "POST";
32987     Roo.apply(this, config);
32988
32989     this.addEvents({
32990     
32991         /**
32992          * @event beforeload
32993          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32994          * @param {Object} This TreeLoader object.
32995          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32996          * @param {Object} callback The callback function specified in the {@link #load} call.
32997          */
32998         beforeload : true,
32999         /**
33000          * @event load
33001          * Fires when the node has been successfuly loaded.
33002          * @param {Object} This TreeLoader object.
33003          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33004          * @param {Object} response The response object containing the data from the server.
33005          */
33006         load : true,
33007         /**
33008          * @event loadexception
33009          * Fires if the network request failed.
33010          * @param {Object} This TreeLoader object.
33011          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33012          * @param {Object} response The response object containing the data from the server.
33013          */
33014         loadexception : true,
33015         /**
33016          * @event create
33017          * Fires before a node is created, enabling you to return custom Node types 
33018          * @param {Object} This TreeLoader object.
33019          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33020          */
33021         create : true
33022     });
33023
33024     Roo.tree.TreeLoader.superclass.constructor.call(this);
33025 };
33026
33027 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33028     /**
33029     * @cfg {String} dataUrl The URL from which to request a Json string which
33030     * specifies an array of node definition object representing the child nodes
33031     * to be loaded.
33032     */
33033     /**
33034     * @cfg {String} requestMethod either GET or POST
33035     * defaults to POST (due to BC)
33036     * to be loaded.
33037     */
33038     /**
33039     * @cfg {Object} baseParams (optional) An object containing properties which
33040     * specify HTTP parameters to be passed to each request for child nodes.
33041     */
33042     /**
33043     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33044     * created by this loader. If the attributes sent by the server have an attribute in this object,
33045     * they take priority.
33046     */
33047     /**
33048     * @cfg {Object} uiProviders (optional) An object containing properties which
33049     * 
33050     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33051     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33052     * <i>uiProvider</i> attribute of a returned child node is a string rather
33053     * than a reference to a TreeNodeUI implementation, this that string value
33054     * is used as a property name in the uiProviders object. You can define the provider named
33055     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33056     */
33057     uiProviders : {},
33058
33059     /**
33060     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33061     * child nodes before loading.
33062     */
33063     clearOnLoad : true,
33064
33065     /**
33066     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33067     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33068     * Grid query { data : [ .....] }
33069     */
33070     
33071     root : false,
33072      /**
33073     * @cfg {String} queryParam (optional) 
33074     * Name of the query as it will be passed on the querystring (defaults to 'node')
33075     * eg. the request will be ?node=[id]
33076     */
33077     
33078     
33079     queryParam: false,
33080     
33081     /**
33082      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33083      * This is called automatically when a node is expanded, but may be used to reload
33084      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33085      * @param {Roo.tree.TreeNode} node
33086      * @param {Function} callback
33087      */
33088     load : function(node, callback){
33089         if(this.clearOnLoad){
33090             while(node.firstChild){
33091                 node.removeChild(node.firstChild);
33092             }
33093         }
33094         if(node.attributes.children){ // preloaded json children
33095             var cs = node.attributes.children;
33096             for(var i = 0, len = cs.length; i < len; i++){
33097                 node.appendChild(this.createNode(cs[i]));
33098             }
33099             if(typeof callback == "function"){
33100                 callback();
33101             }
33102         }else if(this.dataUrl){
33103             this.requestData(node, callback);
33104         }
33105     },
33106
33107     getParams: function(node){
33108         var buf = [], bp = this.baseParams;
33109         for(var key in bp){
33110             if(typeof bp[key] != "function"){
33111                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33112             }
33113         }
33114         var n = this.queryParam === false ? 'node' : this.queryParam;
33115         buf.push(n + "=", encodeURIComponent(node.id));
33116         return buf.join("");
33117     },
33118
33119     requestData : function(node, callback){
33120         if(this.fireEvent("beforeload", this, node, callback) !== false){
33121             this.transId = Roo.Ajax.request({
33122                 method:this.requestMethod,
33123                 url: this.dataUrl||this.url,
33124                 success: this.handleResponse,
33125                 failure: this.handleFailure,
33126                 scope: this,
33127                 argument: {callback: callback, node: node},
33128                 params: this.getParams(node)
33129             });
33130         }else{
33131             // if the load is cancelled, make sure we notify
33132             // the node that we are done
33133             if(typeof callback == "function"){
33134                 callback();
33135             }
33136         }
33137     },
33138
33139     isLoading : function(){
33140         return this.transId ? true : false;
33141     },
33142
33143     abort : function(){
33144         if(this.isLoading()){
33145             Roo.Ajax.abort(this.transId);
33146         }
33147     },
33148
33149     // private
33150     createNode : function(attr)
33151     {
33152         // apply baseAttrs, nice idea Corey!
33153         if(this.baseAttrs){
33154             Roo.applyIf(attr, this.baseAttrs);
33155         }
33156         if(this.applyLoader !== false){
33157             attr.loader = this;
33158         }
33159         // uiProvider = depreciated..
33160         
33161         if(typeof(attr.uiProvider) == 'string'){
33162            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33163                 /**  eval:var:attr */ eval(attr.uiProvider);
33164         }
33165         if(typeof(this.uiProviders['default']) != 'undefined') {
33166             attr.uiProvider = this.uiProviders['default'];
33167         }
33168         
33169         this.fireEvent('create', this, attr);
33170         
33171         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33172         return(attr.leaf ?
33173                         new Roo.tree.TreeNode(attr) :
33174                         new Roo.tree.AsyncTreeNode(attr));
33175     },
33176
33177     processResponse : function(response, node, callback)
33178     {
33179         var json = response.responseText;
33180         try {
33181             
33182             var o = Roo.decode(json);
33183             
33184             if (this.root === false && typeof(o.success) != undefined) {
33185                 this.root = 'data'; // the default behaviour for list like data..
33186                 }
33187                 
33188             if (this.root !== false &&  !o.success) {
33189                 // it's a failure condition.
33190                 var a = response.argument;
33191                 this.fireEvent("loadexception", this, a.node, response);
33192                 Roo.log("Load failed - should have a handler really");
33193                 return;
33194             }
33195             
33196             
33197             
33198             if (this.root !== false) {
33199                  o = o[this.root];
33200             }
33201             
33202             for(var i = 0, len = o.length; i < len; i++){
33203                 var n = this.createNode(o[i]);
33204                 if(n){
33205                     node.appendChild(n);
33206                 }
33207             }
33208             if(typeof callback == "function"){
33209                 callback(this, node);
33210             }
33211         }catch(e){
33212             this.handleFailure(response);
33213         }
33214     },
33215
33216     handleResponse : function(response){
33217         this.transId = false;
33218         var a = response.argument;
33219         this.processResponse(response, a.node, a.callback);
33220         this.fireEvent("load", this, a.node, response);
33221     },
33222
33223     handleFailure : function(response)
33224     {
33225         // should handle failure better..
33226         this.transId = false;
33227         var a = response.argument;
33228         this.fireEvent("loadexception", this, a.node, response);
33229         if(typeof a.callback == "function"){
33230             a.callback(this, a.node);
33231         }
33232     }
33233 });/*
33234  * Based on:
33235  * Ext JS Library 1.1.1
33236  * Copyright(c) 2006-2007, Ext JS, LLC.
33237  *
33238  * Originally Released Under LGPL - original licence link has changed is not relivant.
33239  *
33240  * Fork - LGPL
33241  * <script type="text/javascript">
33242  */
33243
33244 /**
33245 * @class Roo.tree.TreeFilter
33246 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33247 * @param {TreePanel} tree
33248 * @param {Object} config (optional)
33249  */
33250 Roo.tree.TreeFilter = function(tree, config){
33251     this.tree = tree;
33252     this.filtered = {};
33253     Roo.apply(this, config);
33254 };
33255
33256 Roo.tree.TreeFilter.prototype = {
33257     clearBlank:false,
33258     reverse:false,
33259     autoClear:false,
33260     remove:false,
33261
33262      /**
33263      * Filter the data by a specific attribute.
33264      * @param {String/RegExp} value Either string that the attribute value
33265      * should start with or a RegExp to test against the attribute
33266      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33267      * @param {TreeNode} startNode (optional) The node to start the filter at.
33268      */
33269     filter : function(value, attr, startNode){
33270         attr = attr || "text";
33271         var f;
33272         if(typeof value == "string"){
33273             var vlen = value.length;
33274             // auto clear empty filter
33275             if(vlen == 0 && this.clearBlank){
33276                 this.clear();
33277                 return;
33278             }
33279             value = value.toLowerCase();
33280             f = function(n){
33281                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33282             };
33283         }else if(value.exec){ // regex?
33284             f = function(n){
33285                 return value.test(n.attributes[attr]);
33286             };
33287         }else{
33288             throw 'Illegal filter type, must be string or regex';
33289         }
33290         this.filterBy(f, null, startNode);
33291         },
33292
33293     /**
33294      * Filter by a function. The passed function will be called with each
33295      * node in the tree (or from the startNode). If the function returns true, the node is kept
33296      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33297      * @param {Function} fn The filter function
33298      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33299      */
33300     filterBy : function(fn, scope, startNode){
33301         startNode = startNode || this.tree.root;
33302         if(this.autoClear){
33303             this.clear();
33304         }
33305         var af = this.filtered, rv = this.reverse;
33306         var f = function(n){
33307             if(n == startNode){
33308                 return true;
33309             }
33310             if(af[n.id]){
33311                 return false;
33312             }
33313             var m = fn.call(scope || n, n);
33314             if(!m || rv){
33315                 af[n.id] = n;
33316                 n.ui.hide();
33317                 return false;
33318             }
33319             return true;
33320         };
33321         startNode.cascade(f);
33322         if(this.remove){
33323            for(var id in af){
33324                if(typeof id != "function"){
33325                    var n = af[id];
33326                    if(n && n.parentNode){
33327                        n.parentNode.removeChild(n);
33328                    }
33329                }
33330            }
33331         }
33332     },
33333
33334     /**
33335      * Clears the current filter. Note: with the "remove" option
33336      * set a filter cannot be cleared.
33337      */
33338     clear : function(){
33339         var t = this.tree;
33340         var af = this.filtered;
33341         for(var id in af){
33342             if(typeof id != "function"){
33343                 var n = af[id];
33344                 if(n){
33345                     n.ui.show();
33346                 }
33347             }
33348         }
33349         this.filtered = {};
33350     }
33351 };
33352 /*
33353  * Based on:
33354  * Ext JS Library 1.1.1
33355  * Copyright(c) 2006-2007, Ext JS, LLC.
33356  *
33357  * Originally Released Under LGPL - original licence link has changed is not relivant.
33358  *
33359  * Fork - LGPL
33360  * <script type="text/javascript">
33361  */
33362  
33363
33364 /**
33365  * @class Roo.tree.TreeSorter
33366  * Provides sorting of nodes in a TreePanel
33367  * 
33368  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33369  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33370  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33371  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33372  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33373  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33374  * @constructor
33375  * @param {TreePanel} tree
33376  * @param {Object} config
33377  */
33378 Roo.tree.TreeSorter = function(tree, config){
33379     Roo.apply(this, config);
33380     tree.on("beforechildrenrendered", this.doSort, this);
33381     tree.on("append", this.updateSort, this);
33382     tree.on("insert", this.updateSort, this);
33383     
33384     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33385     var p = this.property || "text";
33386     var sortType = this.sortType;
33387     var fs = this.folderSort;
33388     var cs = this.caseSensitive === true;
33389     var leafAttr = this.leafAttr || 'leaf';
33390
33391     this.sortFn = function(n1, n2){
33392         if(fs){
33393             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33394                 return 1;
33395             }
33396             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33397                 return -1;
33398             }
33399         }
33400         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33401         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33402         if(v1 < v2){
33403                         return dsc ? +1 : -1;
33404                 }else if(v1 > v2){
33405                         return dsc ? -1 : +1;
33406         }else{
33407                 return 0;
33408         }
33409     };
33410 };
33411
33412 Roo.tree.TreeSorter.prototype = {
33413     doSort : function(node){
33414         node.sort(this.sortFn);
33415     },
33416     
33417     compareNodes : function(n1, n2){
33418         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33419     },
33420     
33421     updateSort : function(tree, node){
33422         if(node.childrenRendered){
33423             this.doSort.defer(1, this, [node]);
33424         }
33425     }
33426 };/*
33427  * Based on:
33428  * Ext JS Library 1.1.1
33429  * Copyright(c) 2006-2007, Ext JS, LLC.
33430  *
33431  * Originally Released Under LGPL - original licence link has changed is not relivant.
33432  *
33433  * Fork - LGPL
33434  * <script type="text/javascript">
33435  */
33436
33437 if(Roo.dd.DropZone){
33438     
33439 Roo.tree.TreeDropZone = function(tree, config){
33440     this.allowParentInsert = false;
33441     this.allowContainerDrop = false;
33442     this.appendOnly = false;
33443     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33444     this.tree = tree;
33445     this.lastInsertClass = "x-tree-no-status";
33446     this.dragOverData = {};
33447 };
33448
33449 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33450     ddGroup : "TreeDD",
33451     
33452     expandDelay : 1000,
33453     
33454     expandNode : function(node){
33455         if(node.hasChildNodes() && !node.isExpanded()){
33456             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33457         }
33458     },
33459     
33460     queueExpand : function(node){
33461         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33462     },
33463     
33464     cancelExpand : function(){
33465         if(this.expandProcId){
33466             clearTimeout(this.expandProcId);
33467             this.expandProcId = false;
33468         }
33469     },
33470     
33471     isValidDropPoint : function(n, pt, dd, e, data){
33472         if(!n || !data){ return false; }
33473         var targetNode = n.node;
33474         var dropNode = data.node;
33475         // default drop rules
33476         if(!(targetNode && targetNode.isTarget && pt)){
33477             return false;
33478         }
33479         if(pt == "append" && targetNode.allowChildren === false){
33480             return false;
33481         }
33482         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33483             return false;
33484         }
33485         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33486             return false;
33487         }
33488         // reuse the object
33489         var overEvent = this.dragOverData;
33490         overEvent.tree = this.tree;
33491         overEvent.target = targetNode;
33492         overEvent.data = data;
33493         overEvent.point = pt;
33494         overEvent.source = dd;
33495         overEvent.rawEvent = e;
33496         overEvent.dropNode = dropNode;
33497         overEvent.cancel = false;  
33498         var result = this.tree.fireEvent("nodedragover", overEvent);
33499         return overEvent.cancel === false && result !== false;
33500     },
33501     
33502     getDropPoint : function(e, n, dd){
33503         var tn = n.node;
33504         if(tn.isRoot){
33505             return tn.allowChildren !== false ? "append" : false; // always append for root
33506         }
33507         var dragEl = n.ddel;
33508         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33509         var y = Roo.lib.Event.getPageY(e);
33510         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33511         
33512         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33513         var noAppend = tn.allowChildren === false;
33514         if(this.appendOnly || tn.parentNode.allowChildren === false){
33515             return noAppend ? false : "append";
33516         }
33517         var noBelow = false;
33518         if(!this.allowParentInsert){
33519             noBelow = tn.hasChildNodes() && tn.isExpanded();
33520         }
33521         var q = (b - t) / (noAppend ? 2 : 3);
33522         if(y >= t && y < (t + q)){
33523             return "above";
33524         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33525             return "below";
33526         }else{
33527             return "append";
33528         }
33529     },
33530     
33531     onNodeEnter : function(n, dd, e, data){
33532         this.cancelExpand();
33533     },
33534     
33535     onNodeOver : function(n, dd, e, data){
33536         var pt = this.getDropPoint(e, n, dd);
33537         var node = n.node;
33538         
33539         // auto node expand check
33540         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33541             this.queueExpand(node);
33542         }else if(pt != "append"){
33543             this.cancelExpand();
33544         }
33545         
33546         // set the insert point style on the target node
33547         var returnCls = this.dropNotAllowed;
33548         if(this.isValidDropPoint(n, pt, dd, e, data)){
33549            if(pt){
33550                var el = n.ddel;
33551                var cls;
33552                if(pt == "above"){
33553                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33554                    cls = "x-tree-drag-insert-above";
33555                }else if(pt == "below"){
33556                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33557                    cls = "x-tree-drag-insert-below";
33558                }else{
33559                    returnCls = "x-tree-drop-ok-append";
33560                    cls = "x-tree-drag-append";
33561                }
33562                if(this.lastInsertClass != cls){
33563                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33564                    this.lastInsertClass = cls;
33565                }
33566            }
33567        }
33568        return returnCls;
33569     },
33570     
33571     onNodeOut : function(n, dd, e, data){
33572         this.cancelExpand();
33573         this.removeDropIndicators(n);
33574     },
33575     
33576     onNodeDrop : function(n, dd, e, data){
33577         var point = this.getDropPoint(e, n, dd);
33578         var targetNode = n.node;
33579         targetNode.ui.startDrop();
33580         if(!this.isValidDropPoint(n, point, dd, e, data)){
33581             targetNode.ui.endDrop();
33582             return false;
33583         }
33584         // first try to find the drop node
33585         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33586         var dropEvent = {
33587             tree : this.tree,
33588             target: targetNode,
33589             data: data,
33590             point: point,
33591             source: dd,
33592             rawEvent: e,
33593             dropNode: dropNode,
33594             cancel: !dropNode   
33595         };
33596         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33597         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33598             targetNode.ui.endDrop();
33599             return false;
33600         }
33601         // allow target changing
33602         targetNode = dropEvent.target;
33603         if(point == "append" && !targetNode.isExpanded()){
33604             targetNode.expand(false, null, function(){
33605                 this.completeDrop(dropEvent);
33606             }.createDelegate(this));
33607         }else{
33608             this.completeDrop(dropEvent);
33609         }
33610         return true;
33611     },
33612     
33613     completeDrop : function(de){
33614         var ns = de.dropNode, p = de.point, t = de.target;
33615         if(!(ns instanceof Array)){
33616             ns = [ns];
33617         }
33618         var n;
33619         for(var i = 0, len = ns.length; i < len; i++){
33620             n = ns[i];
33621             if(p == "above"){
33622                 t.parentNode.insertBefore(n, t);
33623             }else if(p == "below"){
33624                 t.parentNode.insertBefore(n, t.nextSibling);
33625             }else{
33626                 t.appendChild(n);
33627             }
33628         }
33629         n.ui.focus();
33630         if(this.tree.hlDrop){
33631             n.ui.highlight();
33632         }
33633         t.ui.endDrop();
33634         this.tree.fireEvent("nodedrop", de);
33635     },
33636     
33637     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33638         if(this.tree.hlDrop){
33639             dropNode.ui.focus();
33640             dropNode.ui.highlight();
33641         }
33642         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33643     },
33644     
33645     getTree : function(){
33646         return this.tree;
33647     },
33648     
33649     removeDropIndicators : function(n){
33650         if(n && n.ddel){
33651             var el = n.ddel;
33652             Roo.fly(el).removeClass([
33653                     "x-tree-drag-insert-above",
33654                     "x-tree-drag-insert-below",
33655                     "x-tree-drag-append"]);
33656             this.lastInsertClass = "_noclass";
33657         }
33658     },
33659     
33660     beforeDragDrop : function(target, e, id){
33661         this.cancelExpand();
33662         return true;
33663     },
33664     
33665     afterRepair : function(data){
33666         if(data && Roo.enableFx){
33667             data.node.ui.highlight();
33668         }
33669         this.hideProxy();
33670     }    
33671 });
33672
33673 }
33674 /*
33675  * Based on:
33676  * Ext JS Library 1.1.1
33677  * Copyright(c) 2006-2007, Ext JS, LLC.
33678  *
33679  * Originally Released Under LGPL - original licence link has changed is not relivant.
33680  *
33681  * Fork - LGPL
33682  * <script type="text/javascript">
33683  */
33684  
33685
33686 if(Roo.dd.DragZone){
33687 Roo.tree.TreeDragZone = function(tree, config){
33688     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33689     this.tree = tree;
33690 };
33691
33692 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33693     ddGroup : "TreeDD",
33694     
33695     onBeforeDrag : function(data, e){
33696         var n = data.node;
33697         return n && n.draggable && !n.disabled;
33698     },
33699     
33700     onInitDrag : function(e){
33701         var data = this.dragData;
33702         this.tree.getSelectionModel().select(data.node);
33703         this.proxy.update("");
33704         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33705         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33706     },
33707     
33708     getRepairXY : function(e, data){
33709         return data.node.ui.getDDRepairXY();
33710     },
33711     
33712     onEndDrag : function(data, e){
33713         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33714     },
33715     
33716     onValidDrop : function(dd, e, id){
33717         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33718         this.hideProxy();
33719     },
33720     
33721     beforeInvalidDrop : function(e, id){
33722         // this scrolls the original position back into view
33723         var sm = this.tree.getSelectionModel();
33724         sm.clearSelections();
33725         sm.select(this.dragData.node);
33726     }
33727 });
33728 }/*
33729  * Based on:
33730  * Ext JS Library 1.1.1
33731  * Copyright(c) 2006-2007, Ext JS, LLC.
33732  *
33733  * Originally Released Under LGPL - original licence link has changed is not relivant.
33734  *
33735  * Fork - LGPL
33736  * <script type="text/javascript">
33737  */
33738 /**
33739  * @class Roo.tree.TreeEditor
33740  * @extends Roo.Editor
33741  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33742  * as the editor field.
33743  * @constructor
33744  * @param {Object} config (used to be the tree panel.)
33745  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33746  * 
33747  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33748  * @cfg {Roo.form.TextField|Object} field The field configuration
33749  *
33750  * 
33751  */
33752 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33753     var tree = config;
33754     var field;
33755     if (oldconfig) { // old style..
33756         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33757     } else {
33758         // new style..
33759         tree = config.tree;
33760         config.field = config.field  || {};
33761         config.field.xtype = 'TextField';
33762         field = Roo.factory(config.field, Roo.form);
33763     }
33764     config = config || {};
33765     
33766     
33767     this.addEvents({
33768         /**
33769          * @event beforenodeedit
33770          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33771          * false from the handler of this event.
33772          * @param {Editor} this
33773          * @param {Roo.tree.Node} node 
33774          */
33775         "beforenodeedit" : true
33776     });
33777     
33778     //Roo.log(config);
33779     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33780
33781     this.tree = tree;
33782
33783     tree.on('beforeclick', this.beforeNodeClick, this);
33784     tree.getTreeEl().on('mousedown', this.hide, this);
33785     this.on('complete', this.updateNode, this);
33786     this.on('beforestartedit', this.fitToTree, this);
33787     this.on('startedit', this.bindScroll, this, {delay:10});
33788     this.on('specialkey', this.onSpecialKey, this);
33789 };
33790
33791 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33792     /**
33793      * @cfg {String} alignment
33794      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33795      */
33796     alignment: "l-l",
33797     // inherit
33798     autoSize: false,
33799     /**
33800      * @cfg {Boolean} hideEl
33801      * True to hide the bound element while the editor is displayed (defaults to false)
33802      */
33803     hideEl : false,
33804     /**
33805      * @cfg {String} cls
33806      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33807      */
33808     cls: "x-small-editor x-tree-editor",
33809     /**
33810      * @cfg {Boolean} shim
33811      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33812      */
33813     shim:false,
33814     // inherit
33815     shadow:"frame",
33816     /**
33817      * @cfg {Number} maxWidth
33818      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33819      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33820      * scroll and client offsets into account prior to each edit.
33821      */
33822     maxWidth: 250,
33823
33824     editDelay : 350,
33825
33826     // private
33827     fitToTree : function(ed, el){
33828         var td = this.tree.getTreeEl().dom, nd = el.dom;
33829         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33830             td.scrollLeft = nd.offsetLeft;
33831         }
33832         var w = Math.min(
33833                 this.maxWidth,
33834                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33835         this.setSize(w, '');
33836         
33837         return this.fireEvent('beforenodeedit', this, this.editNode);
33838         
33839     },
33840
33841     // private
33842     triggerEdit : function(node){
33843         this.completeEdit();
33844         this.editNode = node;
33845         this.startEdit(node.ui.textNode, node.text);
33846     },
33847
33848     // private
33849     bindScroll : function(){
33850         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33851     },
33852
33853     // private
33854     beforeNodeClick : function(node, e){
33855         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33856         this.lastClick = new Date();
33857         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33858             e.stopEvent();
33859             this.triggerEdit(node);
33860             return false;
33861         }
33862         return true;
33863     },
33864
33865     // private
33866     updateNode : function(ed, value){
33867         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33868         this.editNode.setText(value);
33869     },
33870
33871     // private
33872     onHide : function(){
33873         Roo.tree.TreeEditor.superclass.onHide.call(this);
33874         if(this.editNode){
33875             this.editNode.ui.focus();
33876         }
33877     },
33878
33879     // private
33880     onSpecialKey : function(field, e){
33881         var k = e.getKey();
33882         if(k == e.ESC){
33883             e.stopEvent();
33884             this.cancelEdit();
33885         }else if(k == e.ENTER && !e.hasModifier()){
33886             e.stopEvent();
33887             this.completeEdit();
33888         }
33889     }
33890 });//<Script type="text/javascript">
33891 /*
33892  * Based on:
33893  * Ext JS Library 1.1.1
33894  * Copyright(c) 2006-2007, Ext JS, LLC.
33895  *
33896  * Originally Released Under LGPL - original licence link has changed is not relivant.
33897  *
33898  * Fork - LGPL
33899  * <script type="text/javascript">
33900  */
33901  
33902 /**
33903  * Not documented??? - probably should be...
33904  */
33905
33906 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33907     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33908     
33909     renderElements : function(n, a, targetNode, bulkRender){
33910         //consel.log("renderElements?");
33911         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33912
33913         var t = n.getOwnerTree();
33914         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33915         
33916         var cols = t.columns;
33917         var bw = t.borderWidth;
33918         var c = cols[0];
33919         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33920          var cb = typeof a.checked == "boolean";
33921         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33922         var colcls = 'x-t-' + tid + '-c0';
33923         var buf = [
33924             '<li class="x-tree-node">',
33925             
33926                 
33927                 '<div class="x-tree-node-el ', a.cls,'">',
33928                     // extran...
33929                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33930                 
33931                 
33932                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33933                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33934                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33935                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33936                            (a.iconCls ? ' '+a.iconCls : ''),
33937                            '" unselectable="on" />',
33938                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33939                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33940                              
33941                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33942                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33943                             '<span unselectable="on" qtip="' + tx + '">',
33944                              tx,
33945                              '</span></a>' ,
33946                     '</div>',
33947                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33948                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33949                  ];
33950         for(var i = 1, len = cols.length; i < len; i++){
33951             c = cols[i];
33952             colcls = 'x-t-' + tid + '-c' +i;
33953             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33954             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33955                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33956                       "</div>");
33957          }
33958          
33959          buf.push(
33960             '</a>',
33961             '<div class="x-clear"></div></div>',
33962             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33963             "</li>");
33964         
33965         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33966             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33967                                 n.nextSibling.ui.getEl(), buf.join(""));
33968         }else{
33969             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33970         }
33971         var el = this.wrap.firstChild;
33972         this.elRow = el;
33973         this.elNode = el.firstChild;
33974         this.ranchor = el.childNodes[1];
33975         this.ctNode = this.wrap.childNodes[1];
33976         var cs = el.firstChild.childNodes;
33977         this.indentNode = cs[0];
33978         this.ecNode = cs[1];
33979         this.iconNode = cs[2];
33980         var index = 3;
33981         if(cb){
33982             this.checkbox = cs[3];
33983             index++;
33984         }
33985         this.anchor = cs[index];
33986         
33987         this.textNode = cs[index].firstChild;
33988         
33989         //el.on("click", this.onClick, this);
33990         //el.on("dblclick", this.onDblClick, this);
33991         
33992         
33993        // console.log(this);
33994     },
33995     initEvents : function(){
33996         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33997         
33998             
33999         var a = this.ranchor;
34000
34001         var el = Roo.get(a);
34002
34003         if(Roo.isOpera){ // opera render bug ignores the CSS
34004             el.setStyle("text-decoration", "none");
34005         }
34006
34007         el.on("click", this.onClick, this);
34008         el.on("dblclick", this.onDblClick, this);
34009         el.on("contextmenu", this.onContextMenu, this);
34010         
34011     },
34012     
34013     /*onSelectedChange : function(state){
34014         if(state){
34015             this.focus();
34016             this.addClass("x-tree-selected");
34017         }else{
34018             //this.blur();
34019             this.removeClass("x-tree-selected");
34020         }
34021     },*/
34022     addClass : function(cls){
34023         if(this.elRow){
34024             Roo.fly(this.elRow).addClass(cls);
34025         }
34026         
34027     },
34028     
34029     
34030     removeClass : function(cls){
34031         if(this.elRow){
34032             Roo.fly(this.elRow).removeClass(cls);
34033         }
34034     }
34035
34036     
34037     
34038 });//<Script type="text/javascript">
34039
34040 /*
34041  * Based on:
34042  * Ext JS Library 1.1.1
34043  * Copyright(c) 2006-2007, Ext JS, LLC.
34044  *
34045  * Originally Released Under LGPL - original licence link has changed is not relivant.
34046  *
34047  * Fork - LGPL
34048  * <script type="text/javascript">
34049  */
34050  
34051
34052 /**
34053  * @class Roo.tree.ColumnTree
34054  * @extends Roo.data.TreePanel
34055  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34056  * @cfg {int} borderWidth  compined right/left border allowance
34057  * @constructor
34058  * @param {String/HTMLElement/Element} el The container element
34059  * @param {Object} config
34060  */
34061 Roo.tree.ColumnTree =  function(el, config)
34062 {
34063    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34064    this.addEvents({
34065         /**
34066         * @event resize
34067         * Fire this event on a container when it resizes
34068         * @param {int} w Width
34069         * @param {int} h Height
34070         */
34071        "resize" : true
34072     });
34073     this.on('resize', this.onResize, this);
34074 };
34075
34076 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34077     //lines:false,
34078     
34079     
34080     borderWidth: Roo.isBorderBox ? 0 : 2, 
34081     headEls : false,
34082     
34083     render : function(){
34084         // add the header.....
34085        
34086         Roo.tree.ColumnTree.superclass.render.apply(this);
34087         
34088         this.el.addClass('x-column-tree');
34089         
34090         this.headers = this.el.createChild(
34091             {cls:'x-tree-headers'},this.innerCt.dom);
34092    
34093         var cols = this.columns, c;
34094         var totalWidth = 0;
34095         this.headEls = [];
34096         var  len = cols.length;
34097         for(var i = 0; i < len; i++){
34098              c = cols[i];
34099              totalWidth += c.width;
34100             this.headEls.push(this.headers.createChild({
34101                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34102                  cn: {
34103                      cls:'x-tree-hd-text',
34104                      html: c.header
34105                  },
34106                  style:'width:'+(c.width-this.borderWidth)+'px;'
34107              }));
34108         }
34109         this.headers.createChild({cls:'x-clear'});
34110         // prevent floats from wrapping when clipped
34111         this.headers.setWidth(totalWidth);
34112         //this.innerCt.setWidth(totalWidth);
34113         this.innerCt.setStyle({ overflow: 'auto' });
34114         this.onResize(this.width, this.height);
34115              
34116         
34117     },
34118     onResize : function(w,h)
34119     {
34120         this.height = h;
34121         this.width = w;
34122         // resize cols..
34123         this.innerCt.setWidth(this.width);
34124         this.innerCt.setHeight(this.height-20);
34125         
34126         // headers...
34127         var cols = this.columns, c;
34128         var totalWidth = 0;
34129         var expEl = false;
34130         var len = cols.length;
34131         for(var i = 0; i < len; i++){
34132             c = cols[i];
34133             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34134                 // it's the expander..
34135                 expEl  = this.headEls[i];
34136                 continue;
34137             }
34138             totalWidth += c.width;
34139             
34140         }
34141         if (expEl) {
34142             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34143         }
34144         this.headers.setWidth(w-20);
34145
34146         
34147         
34148         
34149     }
34150 });
34151 /*
34152  * Based on:
34153  * Ext JS Library 1.1.1
34154  * Copyright(c) 2006-2007, Ext JS, LLC.
34155  *
34156  * Originally Released Under LGPL - original licence link has changed is not relivant.
34157  *
34158  * Fork - LGPL
34159  * <script type="text/javascript">
34160  */
34161  
34162 /**
34163  * @class Roo.menu.Menu
34164  * @extends Roo.util.Observable
34165  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34166  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34167  * @constructor
34168  * Creates a new Menu
34169  * @param {Object} config Configuration options
34170  */
34171 Roo.menu.Menu = function(config){
34172     Roo.apply(this, config);
34173     this.id = this.id || Roo.id();
34174     this.addEvents({
34175         /**
34176          * @event beforeshow
34177          * Fires before this menu is displayed
34178          * @param {Roo.menu.Menu} this
34179          */
34180         beforeshow : true,
34181         /**
34182          * @event beforehide
34183          * Fires before this menu is hidden
34184          * @param {Roo.menu.Menu} this
34185          */
34186         beforehide : true,
34187         /**
34188          * @event show
34189          * Fires after this menu is displayed
34190          * @param {Roo.menu.Menu} this
34191          */
34192         show : true,
34193         /**
34194          * @event hide
34195          * Fires after this menu is hidden
34196          * @param {Roo.menu.Menu} this
34197          */
34198         hide : true,
34199         /**
34200          * @event click
34201          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34202          * @param {Roo.menu.Menu} this
34203          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34204          * @param {Roo.EventObject} e
34205          */
34206         click : true,
34207         /**
34208          * @event mouseover
34209          * Fires when the mouse is hovering over this menu
34210          * @param {Roo.menu.Menu} this
34211          * @param {Roo.EventObject} e
34212          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34213          */
34214         mouseover : true,
34215         /**
34216          * @event mouseout
34217          * Fires when the mouse exits this menu
34218          * @param {Roo.menu.Menu} this
34219          * @param {Roo.EventObject} e
34220          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34221          */
34222         mouseout : true,
34223         /**
34224          * @event itemclick
34225          * Fires when a menu item contained in this menu is clicked
34226          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34227          * @param {Roo.EventObject} e
34228          */
34229         itemclick: true
34230     });
34231     if (this.registerMenu) {
34232         Roo.menu.MenuMgr.register(this);
34233     }
34234     
34235     var mis = this.items;
34236     this.items = new Roo.util.MixedCollection();
34237     if(mis){
34238         this.add.apply(this, mis);
34239     }
34240 };
34241
34242 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34243     /**
34244      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34245      */
34246     minWidth : 120,
34247     /**
34248      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34249      * for bottom-right shadow (defaults to "sides")
34250      */
34251     shadow : "sides",
34252     /**
34253      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34254      * this menu (defaults to "tl-tr?")
34255      */
34256     subMenuAlign : "tl-tr?",
34257     /**
34258      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34259      * relative to its element of origin (defaults to "tl-bl?")
34260      */
34261     defaultAlign : "tl-bl?",
34262     /**
34263      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34264      */
34265     allowOtherMenus : false,
34266     /**
34267      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34268      */
34269     registerMenu : true,
34270
34271     hidden:true,
34272
34273     // private
34274     render : function(){
34275         if(this.el){
34276             return;
34277         }
34278         var el = this.el = new Roo.Layer({
34279             cls: "x-menu",
34280             shadow:this.shadow,
34281             constrain: false,
34282             parentEl: this.parentEl || document.body,
34283             zindex:15000
34284         });
34285
34286         this.keyNav = new Roo.menu.MenuNav(this);
34287
34288         if(this.plain){
34289             el.addClass("x-menu-plain");
34290         }
34291         if(this.cls){
34292             el.addClass(this.cls);
34293         }
34294         // generic focus element
34295         this.focusEl = el.createChild({
34296             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34297         });
34298         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34299         ul.on("click", this.onClick, this);
34300         ul.on("mouseover", this.onMouseOver, this);
34301         ul.on("mouseout", this.onMouseOut, this);
34302         this.items.each(function(item){
34303             var li = document.createElement("li");
34304             li.className = "x-menu-list-item";
34305             ul.dom.appendChild(li);
34306             item.render(li, this);
34307         }, this);
34308         this.ul = ul;
34309         this.autoWidth();
34310     },
34311
34312     // private
34313     autoWidth : function(){
34314         var el = this.el, ul = this.ul;
34315         if(!el){
34316             return;
34317         }
34318         var w = this.width;
34319         if(w){
34320             el.setWidth(w);
34321         }else if(Roo.isIE){
34322             el.setWidth(this.minWidth);
34323             var t = el.dom.offsetWidth; // force recalc
34324             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34325         }
34326     },
34327
34328     // private
34329     delayAutoWidth : function(){
34330         if(this.rendered){
34331             if(!this.awTask){
34332                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34333             }
34334             this.awTask.delay(20);
34335         }
34336     },
34337
34338     // private
34339     findTargetItem : function(e){
34340         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34341         if(t && t.menuItemId){
34342             return this.items.get(t.menuItemId);
34343         }
34344     },
34345
34346     // private
34347     onClick : function(e){
34348         var t;
34349         if(t = this.findTargetItem(e)){
34350             t.onClick(e);
34351             this.fireEvent("click", this, t, e);
34352         }
34353     },
34354
34355     // private
34356     setActiveItem : function(item, autoExpand){
34357         if(item != this.activeItem){
34358             if(this.activeItem){
34359                 this.activeItem.deactivate();
34360             }
34361             this.activeItem = item;
34362             item.activate(autoExpand);
34363         }else if(autoExpand){
34364             item.expandMenu();
34365         }
34366     },
34367
34368     // private
34369     tryActivate : function(start, step){
34370         var items = this.items;
34371         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34372             var item = items.get(i);
34373             if(!item.disabled && item.canActivate){
34374                 this.setActiveItem(item, false);
34375                 return item;
34376             }
34377         }
34378         return false;
34379     },
34380
34381     // private
34382     onMouseOver : function(e){
34383         var t;
34384         if(t = this.findTargetItem(e)){
34385             if(t.canActivate && !t.disabled){
34386                 this.setActiveItem(t, true);
34387             }
34388         }
34389         this.fireEvent("mouseover", this, e, t);
34390     },
34391
34392     // private
34393     onMouseOut : function(e){
34394         var t;
34395         if(t = this.findTargetItem(e)){
34396             if(t == this.activeItem && t.shouldDeactivate(e)){
34397                 this.activeItem.deactivate();
34398                 delete this.activeItem;
34399             }
34400         }
34401         this.fireEvent("mouseout", this, e, t);
34402     },
34403
34404     /**
34405      * Read-only.  Returns true if the menu is currently displayed, else false.
34406      * @type Boolean
34407      */
34408     isVisible : function(){
34409         return this.el && !this.hidden;
34410     },
34411
34412     /**
34413      * Displays this menu relative to another element
34414      * @param {String/HTMLElement/Roo.Element} element The element to align to
34415      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34416      * the element (defaults to this.defaultAlign)
34417      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34418      */
34419     show : function(el, pos, parentMenu){
34420         this.parentMenu = parentMenu;
34421         if(!this.el){
34422             this.render();
34423         }
34424         this.fireEvent("beforeshow", this);
34425         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34426     },
34427
34428     /**
34429      * Displays this menu at a specific xy position
34430      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34431      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34432      */
34433     showAt : function(xy, parentMenu, /* private: */_e){
34434         this.parentMenu = parentMenu;
34435         if(!this.el){
34436             this.render();
34437         }
34438         if(_e !== false){
34439             this.fireEvent("beforeshow", this);
34440             xy = this.el.adjustForConstraints(xy);
34441         }
34442         this.el.setXY(xy);
34443         this.el.show();
34444         this.hidden = false;
34445         this.focus();
34446         this.fireEvent("show", this);
34447     },
34448
34449     focus : function(){
34450         if(!this.hidden){
34451             this.doFocus.defer(50, this);
34452         }
34453     },
34454
34455     doFocus : function(){
34456         if(!this.hidden){
34457             this.focusEl.focus();
34458         }
34459     },
34460
34461     /**
34462      * Hides this menu and optionally all parent menus
34463      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34464      */
34465     hide : function(deep){
34466         if(this.el && this.isVisible()){
34467             this.fireEvent("beforehide", this);
34468             if(this.activeItem){
34469                 this.activeItem.deactivate();
34470                 this.activeItem = null;
34471             }
34472             this.el.hide();
34473             this.hidden = true;
34474             this.fireEvent("hide", this);
34475         }
34476         if(deep === true && this.parentMenu){
34477             this.parentMenu.hide(true);
34478         }
34479     },
34480
34481     /**
34482      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34483      * Any of the following are valid:
34484      * <ul>
34485      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34486      * <li>An HTMLElement object which will be converted to a menu item</li>
34487      * <li>A menu item config object that will be created as a new menu item</li>
34488      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34489      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34490      * </ul>
34491      * Usage:
34492      * <pre><code>
34493 // Create the menu
34494 var menu = new Roo.menu.Menu();
34495
34496 // Create a menu item to add by reference
34497 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34498
34499 // Add a bunch of items at once using different methods.
34500 // Only the last item added will be returned.
34501 var item = menu.add(
34502     menuItem,                // add existing item by ref
34503     'Dynamic Item',          // new TextItem
34504     '-',                     // new separator
34505     { text: 'Config Item' }  // new item by config
34506 );
34507 </code></pre>
34508      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34509      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34510      */
34511     add : function(){
34512         var a = arguments, l = a.length, item;
34513         for(var i = 0; i < l; i++){
34514             var el = a[i];
34515             if ((typeof(el) == "object") && el.xtype && el.xns) {
34516                 el = Roo.factory(el, Roo.menu);
34517             }
34518             
34519             if(el.render){ // some kind of Item
34520                 item = this.addItem(el);
34521             }else if(typeof el == "string"){ // string
34522                 if(el == "separator" || el == "-"){
34523                     item = this.addSeparator();
34524                 }else{
34525                     item = this.addText(el);
34526                 }
34527             }else if(el.tagName || el.el){ // element
34528                 item = this.addElement(el);
34529             }else if(typeof el == "object"){ // must be menu item config?
34530                 item = this.addMenuItem(el);
34531             }
34532         }
34533         return item;
34534     },
34535
34536     /**
34537      * Returns this menu's underlying {@link Roo.Element} object
34538      * @return {Roo.Element} The element
34539      */
34540     getEl : function(){
34541         if(!this.el){
34542             this.render();
34543         }
34544         return this.el;
34545     },
34546
34547     /**
34548      * Adds a separator bar to the menu
34549      * @return {Roo.menu.Item} The menu item that was added
34550      */
34551     addSeparator : function(){
34552         return this.addItem(new Roo.menu.Separator());
34553     },
34554
34555     /**
34556      * Adds an {@link Roo.Element} object to the menu
34557      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34558      * @return {Roo.menu.Item} The menu item that was added
34559      */
34560     addElement : function(el){
34561         return this.addItem(new Roo.menu.BaseItem(el));
34562     },
34563
34564     /**
34565      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34566      * @param {Roo.menu.Item} item The menu item to add
34567      * @return {Roo.menu.Item} The menu item that was added
34568      */
34569     addItem : function(item){
34570         this.items.add(item);
34571         if(this.ul){
34572             var li = document.createElement("li");
34573             li.className = "x-menu-list-item";
34574             this.ul.dom.appendChild(li);
34575             item.render(li, this);
34576             this.delayAutoWidth();
34577         }
34578         return item;
34579     },
34580
34581     /**
34582      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34583      * @param {Object} config A MenuItem config object
34584      * @return {Roo.menu.Item} The menu item that was added
34585      */
34586     addMenuItem : function(config){
34587         if(!(config instanceof Roo.menu.Item)){
34588             if(typeof config.checked == "boolean"){ // must be check menu item config?
34589                 config = new Roo.menu.CheckItem(config);
34590             }else{
34591                 config = new Roo.menu.Item(config);
34592             }
34593         }
34594         return this.addItem(config);
34595     },
34596
34597     /**
34598      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34599      * @param {String} text The text to display in the menu item
34600      * @return {Roo.menu.Item} The menu item that was added
34601      */
34602     addText : function(text){
34603         return this.addItem(new Roo.menu.TextItem({ text : text }));
34604     },
34605
34606     /**
34607      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34608      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34609      * @param {Roo.menu.Item} item The menu item to add
34610      * @return {Roo.menu.Item} The menu item that was added
34611      */
34612     insert : function(index, item){
34613         this.items.insert(index, item);
34614         if(this.ul){
34615             var li = document.createElement("li");
34616             li.className = "x-menu-list-item";
34617             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34618             item.render(li, this);
34619             this.delayAutoWidth();
34620         }
34621         return item;
34622     },
34623
34624     /**
34625      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34626      * @param {Roo.menu.Item} item The menu item to remove
34627      */
34628     remove : function(item){
34629         this.items.removeKey(item.id);
34630         item.destroy();
34631     },
34632
34633     /**
34634      * Removes and destroys all items in the menu
34635      */
34636     removeAll : function(){
34637         var f;
34638         while(f = this.items.first()){
34639             this.remove(f);
34640         }
34641     }
34642 });
34643
34644 // MenuNav is a private utility class used internally by the Menu
34645 Roo.menu.MenuNav = function(menu){
34646     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34647     this.scope = this.menu = menu;
34648 };
34649
34650 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34651     doRelay : function(e, h){
34652         var k = e.getKey();
34653         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34654             this.menu.tryActivate(0, 1);
34655             return false;
34656         }
34657         return h.call(this.scope || this, e, this.menu);
34658     },
34659
34660     up : function(e, m){
34661         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34662             m.tryActivate(m.items.length-1, -1);
34663         }
34664     },
34665
34666     down : function(e, m){
34667         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34668             m.tryActivate(0, 1);
34669         }
34670     },
34671
34672     right : function(e, m){
34673         if(m.activeItem){
34674             m.activeItem.expandMenu(true);
34675         }
34676     },
34677
34678     left : function(e, m){
34679         m.hide();
34680         if(m.parentMenu && m.parentMenu.activeItem){
34681             m.parentMenu.activeItem.activate();
34682         }
34683     },
34684
34685     enter : function(e, m){
34686         if(m.activeItem){
34687             e.stopPropagation();
34688             m.activeItem.onClick(e);
34689             m.fireEvent("click", this, m.activeItem);
34690             return true;
34691         }
34692     }
34693 });/*
34694  * Based on:
34695  * Ext JS Library 1.1.1
34696  * Copyright(c) 2006-2007, Ext JS, LLC.
34697  *
34698  * Originally Released Under LGPL - original licence link has changed is not relivant.
34699  *
34700  * Fork - LGPL
34701  * <script type="text/javascript">
34702  */
34703  
34704 /**
34705  * @class Roo.menu.MenuMgr
34706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34707  * @singleton
34708  */
34709 Roo.menu.MenuMgr = function(){
34710    var menus, active, groups = {}, attached = false, lastShow = new Date();
34711
34712    // private - called when first menu is created
34713    function init(){
34714        menus = {};
34715        active = new Roo.util.MixedCollection();
34716        Roo.get(document).addKeyListener(27, function(){
34717            if(active.length > 0){
34718                hideAll();
34719            }
34720        });
34721    }
34722
34723    // private
34724    function hideAll(){
34725        if(active && active.length > 0){
34726            var c = active.clone();
34727            c.each(function(m){
34728                m.hide();
34729            });
34730        }
34731    }
34732
34733    // private
34734    function onHide(m){
34735        active.remove(m);
34736        if(active.length < 1){
34737            Roo.get(document).un("mousedown", onMouseDown);
34738            attached = false;
34739        }
34740    }
34741
34742    // private
34743    function onShow(m){
34744        var last = active.last();
34745        lastShow = new Date();
34746        active.add(m);
34747        if(!attached){
34748            Roo.get(document).on("mousedown", onMouseDown);
34749            attached = true;
34750        }
34751        if(m.parentMenu){
34752           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34753           m.parentMenu.activeChild = m;
34754        }else if(last && last.isVisible()){
34755           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34756        }
34757    }
34758
34759    // private
34760    function onBeforeHide(m){
34761        if(m.activeChild){
34762            m.activeChild.hide();
34763        }
34764        if(m.autoHideTimer){
34765            clearTimeout(m.autoHideTimer);
34766            delete m.autoHideTimer;
34767        }
34768    }
34769
34770    // private
34771    function onBeforeShow(m){
34772        var pm = m.parentMenu;
34773        if(!pm && !m.allowOtherMenus){
34774            hideAll();
34775        }else if(pm && pm.activeChild && active != m){
34776            pm.activeChild.hide();
34777        }
34778    }
34779
34780    // private
34781    function onMouseDown(e){
34782        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34783            hideAll();
34784        }
34785    }
34786
34787    // private
34788    function onBeforeCheck(mi, state){
34789        if(state){
34790            var g = groups[mi.group];
34791            for(var i = 0, l = g.length; i < l; i++){
34792                if(g[i] != mi){
34793                    g[i].setChecked(false);
34794                }
34795            }
34796        }
34797    }
34798
34799    return {
34800
34801        /**
34802         * Hides all menus that are currently visible
34803         */
34804        hideAll : function(){
34805             hideAll();  
34806        },
34807
34808        // private
34809        register : function(menu){
34810            if(!menus){
34811                init();
34812            }
34813            menus[menu.id] = menu;
34814            menu.on("beforehide", onBeforeHide);
34815            menu.on("hide", onHide);
34816            menu.on("beforeshow", onBeforeShow);
34817            menu.on("show", onShow);
34818            var g = menu.group;
34819            if(g && menu.events["checkchange"]){
34820                if(!groups[g]){
34821                    groups[g] = [];
34822                }
34823                groups[g].push(menu);
34824                menu.on("checkchange", onCheck);
34825            }
34826        },
34827
34828         /**
34829          * Returns a {@link Roo.menu.Menu} object
34830          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34831          * be used to generate and return a new Menu instance.
34832          */
34833        get : function(menu){
34834            if(typeof menu == "string"){ // menu id
34835                return menus[menu];
34836            }else if(menu.events){  // menu instance
34837                return menu;
34838            }else if(typeof menu.length == 'number'){ // array of menu items?
34839                return new Roo.menu.Menu({items:menu});
34840            }else{ // otherwise, must be a config
34841                return new Roo.menu.Menu(menu);
34842            }
34843        },
34844
34845        // private
34846        unregister : function(menu){
34847            delete menus[menu.id];
34848            menu.un("beforehide", onBeforeHide);
34849            menu.un("hide", onHide);
34850            menu.un("beforeshow", onBeforeShow);
34851            menu.un("show", onShow);
34852            var g = menu.group;
34853            if(g && menu.events["checkchange"]){
34854                groups[g].remove(menu);
34855                menu.un("checkchange", onCheck);
34856            }
34857        },
34858
34859        // private
34860        registerCheckable : function(menuItem){
34861            var g = menuItem.group;
34862            if(g){
34863                if(!groups[g]){
34864                    groups[g] = [];
34865                }
34866                groups[g].push(menuItem);
34867                menuItem.on("beforecheckchange", onBeforeCheck);
34868            }
34869        },
34870
34871        // private
34872        unregisterCheckable : function(menuItem){
34873            var g = menuItem.group;
34874            if(g){
34875                groups[g].remove(menuItem);
34876                menuItem.un("beforecheckchange", onBeforeCheck);
34877            }
34878        }
34879    };
34880 }();/*
34881  * Based on:
34882  * Ext JS Library 1.1.1
34883  * Copyright(c) 2006-2007, Ext JS, LLC.
34884  *
34885  * Originally Released Under LGPL - original licence link has changed is not relivant.
34886  *
34887  * Fork - LGPL
34888  * <script type="text/javascript">
34889  */
34890  
34891
34892 /**
34893  * @class Roo.menu.BaseItem
34894  * @extends Roo.Component
34895  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34896  * management and base configuration options shared by all menu components.
34897  * @constructor
34898  * Creates a new BaseItem
34899  * @param {Object} config Configuration options
34900  */
34901 Roo.menu.BaseItem = function(config){
34902     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34903
34904     this.addEvents({
34905         /**
34906          * @event click
34907          * Fires when this item is clicked
34908          * @param {Roo.menu.BaseItem} this
34909          * @param {Roo.EventObject} e
34910          */
34911         click: true,
34912         /**
34913          * @event activate
34914          * Fires when this item is activated
34915          * @param {Roo.menu.BaseItem} this
34916          */
34917         activate : true,
34918         /**
34919          * @event deactivate
34920          * Fires when this item is deactivated
34921          * @param {Roo.menu.BaseItem} this
34922          */
34923         deactivate : true
34924     });
34925
34926     if(this.handler){
34927         this.on("click", this.handler, this.scope, true);
34928     }
34929 };
34930
34931 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34932     /**
34933      * @cfg {Function} handler
34934      * A function that will handle the click event of this menu item (defaults to undefined)
34935      */
34936     /**
34937      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34938      */
34939     canActivate : false,
34940     /**
34941      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34942      */
34943     activeClass : "x-menu-item-active",
34944     /**
34945      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34946      */
34947     hideOnClick : true,
34948     /**
34949      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34950      */
34951     hideDelay : 100,
34952
34953     // private
34954     ctype: "Roo.menu.BaseItem",
34955
34956     // private
34957     actionMode : "container",
34958
34959     // private
34960     render : function(container, parentMenu){
34961         this.parentMenu = parentMenu;
34962         Roo.menu.BaseItem.superclass.render.call(this, container);
34963         this.container.menuItemId = this.id;
34964     },
34965
34966     // private
34967     onRender : function(container, position){
34968         this.el = Roo.get(this.el);
34969         container.dom.appendChild(this.el.dom);
34970     },
34971
34972     // private
34973     onClick : function(e){
34974         if(!this.disabled && this.fireEvent("click", this, e) !== false
34975                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34976             this.handleClick(e);
34977         }else{
34978             e.stopEvent();
34979         }
34980     },
34981
34982     // private
34983     activate : function(){
34984         if(this.disabled){
34985             return false;
34986         }
34987         var li = this.container;
34988         li.addClass(this.activeClass);
34989         this.region = li.getRegion().adjust(2, 2, -2, -2);
34990         this.fireEvent("activate", this);
34991         return true;
34992     },
34993
34994     // private
34995     deactivate : function(){
34996         this.container.removeClass(this.activeClass);
34997         this.fireEvent("deactivate", this);
34998     },
34999
35000     // private
35001     shouldDeactivate : function(e){
35002         return !this.region || !this.region.contains(e.getPoint());
35003     },
35004
35005     // private
35006     handleClick : function(e){
35007         if(this.hideOnClick){
35008             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35009         }
35010     },
35011
35012     // private
35013     expandMenu : function(autoActivate){
35014         // do nothing
35015     },
35016
35017     // private
35018     hideMenu : function(){
35019         // do nothing
35020     }
35021 });/*
35022  * Based on:
35023  * Ext JS Library 1.1.1
35024  * Copyright(c) 2006-2007, Ext JS, LLC.
35025  *
35026  * Originally Released Under LGPL - original licence link has changed is not relivant.
35027  *
35028  * Fork - LGPL
35029  * <script type="text/javascript">
35030  */
35031  
35032 /**
35033  * @class Roo.menu.Adapter
35034  * @extends Roo.menu.BaseItem
35035  * 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.
35036  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35037  * @constructor
35038  * Creates a new Adapter
35039  * @param {Object} config Configuration options
35040  */
35041 Roo.menu.Adapter = function(component, config){
35042     Roo.menu.Adapter.superclass.constructor.call(this, config);
35043     this.component = component;
35044 };
35045 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35046     // private
35047     canActivate : true,
35048
35049     // private
35050     onRender : function(container, position){
35051         this.component.render(container);
35052         this.el = this.component.getEl();
35053     },
35054
35055     // private
35056     activate : function(){
35057         if(this.disabled){
35058             return false;
35059         }
35060         this.component.focus();
35061         this.fireEvent("activate", this);
35062         return true;
35063     },
35064
35065     // private
35066     deactivate : function(){
35067         this.fireEvent("deactivate", this);
35068     },
35069
35070     // private
35071     disable : function(){
35072         this.component.disable();
35073         Roo.menu.Adapter.superclass.disable.call(this);
35074     },
35075
35076     // private
35077     enable : function(){
35078         this.component.enable();
35079         Roo.menu.Adapter.superclass.enable.call(this);
35080     }
35081 });/*
35082  * Based on:
35083  * Ext JS Library 1.1.1
35084  * Copyright(c) 2006-2007, Ext JS, LLC.
35085  *
35086  * Originally Released Under LGPL - original licence link has changed is not relivant.
35087  *
35088  * Fork - LGPL
35089  * <script type="text/javascript">
35090  */
35091
35092 /**
35093  * @class Roo.menu.TextItem
35094  * @extends Roo.menu.BaseItem
35095  * Adds a static text string to a menu, usually used as either a heading or group separator.
35096  * Note: old style constructor with text is still supported.
35097  * 
35098  * @constructor
35099  * Creates a new TextItem
35100  * @param {Object} cfg Configuration
35101  */
35102 Roo.menu.TextItem = function(cfg){
35103     if (typeof(cfg) == 'string') {
35104         this.text = cfg;
35105     } else {
35106         Roo.apply(this,cfg);
35107     }
35108     
35109     Roo.menu.TextItem.superclass.constructor.call(this);
35110 };
35111
35112 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35113     /**
35114      * @cfg {Boolean} text Text to show on item.
35115      */
35116     text : '',
35117     
35118     /**
35119      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35120      */
35121     hideOnClick : false,
35122     /**
35123      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35124      */
35125     itemCls : "x-menu-text",
35126
35127     // private
35128     onRender : function(){
35129         var s = document.createElement("span");
35130         s.className = this.itemCls;
35131         s.innerHTML = this.text;
35132         this.el = s;
35133         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35134     }
35135 });/*
35136  * Based on:
35137  * Ext JS Library 1.1.1
35138  * Copyright(c) 2006-2007, Ext JS, LLC.
35139  *
35140  * Originally Released Under LGPL - original licence link has changed is not relivant.
35141  *
35142  * Fork - LGPL
35143  * <script type="text/javascript">
35144  */
35145
35146 /**
35147  * @class Roo.menu.Separator
35148  * @extends Roo.menu.BaseItem
35149  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35150  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35151  * @constructor
35152  * @param {Object} config Configuration options
35153  */
35154 Roo.menu.Separator = function(config){
35155     Roo.menu.Separator.superclass.constructor.call(this, config);
35156 };
35157
35158 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35159     /**
35160      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35161      */
35162     itemCls : "x-menu-sep",
35163     /**
35164      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35165      */
35166     hideOnClick : false,
35167
35168     // private
35169     onRender : function(li){
35170         var s = document.createElement("span");
35171         s.className = this.itemCls;
35172         s.innerHTML = "&#160;";
35173         this.el = s;
35174         li.addClass("x-menu-sep-li");
35175         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35176     }
35177 });/*
35178  * Based on:
35179  * Ext JS Library 1.1.1
35180  * Copyright(c) 2006-2007, Ext JS, LLC.
35181  *
35182  * Originally Released Under LGPL - original licence link has changed is not relivant.
35183  *
35184  * Fork - LGPL
35185  * <script type="text/javascript">
35186  */
35187 /**
35188  * @class Roo.menu.Item
35189  * @extends Roo.menu.BaseItem
35190  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35191  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35192  * activation and click handling.
35193  * @constructor
35194  * Creates a new Item
35195  * @param {Object} config Configuration options
35196  */
35197 Roo.menu.Item = function(config){
35198     Roo.menu.Item.superclass.constructor.call(this, config);
35199     if(this.menu){
35200         this.menu = Roo.menu.MenuMgr.get(this.menu);
35201     }
35202 };
35203 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35204     
35205     /**
35206      * @cfg {String} text
35207      * The text to show on the menu item.
35208      */
35209     text: '',
35210      /**
35211      * @cfg {String} HTML to render in menu
35212      * The text to show on the menu item (HTML version).
35213      */
35214     html: '',
35215     /**
35216      * @cfg {String} icon
35217      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35218      */
35219     icon: undefined,
35220     /**
35221      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35222      */
35223     itemCls : "x-menu-item",
35224     /**
35225      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35226      */
35227     canActivate : true,
35228     /**
35229      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35230      */
35231     showDelay: 200,
35232     // doc'd in BaseItem
35233     hideDelay: 200,
35234
35235     // private
35236     ctype: "Roo.menu.Item",
35237     
35238     // private
35239     onRender : function(container, position){
35240         var el = document.createElement("a");
35241         el.hideFocus = true;
35242         el.unselectable = "on";
35243         el.href = this.href || "#";
35244         if(this.hrefTarget){
35245             el.target = this.hrefTarget;
35246         }
35247         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35248         
35249         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35250         
35251         el.innerHTML = String.format(
35252                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35253                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35254         this.el = el;
35255         Roo.menu.Item.superclass.onRender.call(this, container, position);
35256     },
35257
35258     /**
35259      * Sets the text to display in this menu item
35260      * @param {String} text The text to display
35261      * @param {Boolean} isHTML true to indicate text is pure html.
35262      */
35263     setText : function(text, isHTML){
35264         if (isHTML) {
35265             this.html = text;
35266         } else {
35267             this.text = text;
35268             this.html = '';
35269         }
35270         if(this.rendered){
35271             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35272      
35273             this.el.update(String.format(
35274                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35275                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35276             this.parentMenu.autoWidth();
35277         }
35278     },
35279
35280     // private
35281     handleClick : function(e){
35282         if(!this.href){ // if no link defined, stop the event automatically
35283             e.stopEvent();
35284         }
35285         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35286     },
35287
35288     // private
35289     activate : function(autoExpand){
35290         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35291             this.focus();
35292             if(autoExpand){
35293                 this.expandMenu();
35294             }
35295         }
35296         return true;
35297     },
35298
35299     // private
35300     shouldDeactivate : function(e){
35301         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35302             if(this.menu && this.menu.isVisible()){
35303                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35304             }
35305             return true;
35306         }
35307         return false;
35308     },
35309
35310     // private
35311     deactivate : function(){
35312         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35313         this.hideMenu();
35314     },
35315
35316     // private
35317     expandMenu : function(autoActivate){
35318         if(!this.disabled && this.menu){
35319             clearTimeout(this.hideTimer);
35320             delete this.hideTimer;
35321             if(!this.menu.isVisible() && !this.showTimer){
35322                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35323             }else if (this.menu.isVisible() && autoActivate){
35324                 this.menu.tryActivate(0, 1);
35325             }
35326         }
35327     },
35328
35329     // private
35330     deferExpand : function(autoActivate){
35331         delete this.showTimer;
35332         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35333         if(autoActivate){
35334             this.menu.tryActivate(0, 1);
35335         }
35336     },
35337
35338     // private
35339     hideMenu : function(){
35340         clearTimeout(this.showTimer);
35341         delete this.showTimer;
35342         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35343             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35344         }
35345     },
35346
35347     // private
35348     deferHide : function(){
35349         delete this.hideTimer;
35350         this.menu.hide();
35351     }
35352 });/*
35353  * Based on:
35354  * Ext JS Library 1.1.1
35355  * Copyright(c) 2006-2007, Ext JS, LLC.
35356  *
35357  * Originally Released Under LGPL - original licence link has changed is not relivant.
35358  *
35359  * Fork - LGPL
35360  * <script type="text/javascript">
35361  */
35362  
35363 /**
35364  * @class Roo.menu.CheckItem
35365  * @extends Roo.menu.Item
35366  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35367  * @constructor
35368  * Creates a new CheckItem
35369  * @param {Object} config Configuration options
35370  */
35371 Roo.menu.CheckItem = function(config){
35372     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35373     this.addEvents({
35374         /**
35375          * @event beforecheckchange
35376          * Fires before the checked value is set, providing an opportunity to cancel if needed
35377          * @param {Roo.menu.CheckItem} this
35378          * @param {Boolean} checked The new checked value that will be set
35379          */
35380         "beforecheckchange" : true,
35381         /**
35382          * @event checkchange
35383          * Fires after the checked value has been set
35384          * @param {Roo.menu.CheckItem} this
35385          * @param {Boolean} checked The checked value that was set
35386          */
35387         "checkchange" : true
35388     });
35389     if(this.checkHandler){
35390         this.on('checkchange', this.checkHandler, this.scope);
35391     }
35392 };
35393 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35394     /**
35395      * @cfg {String} group
35396      * All check items with the same group name will automatically be grouped into a single-select
35397      * radio button group (defaults to '')
35398      */
35399     /**
35400      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35401      */
35402     itemCls : "x-menu-item x-menu-check-item",
35403     /**
35404      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35405      */
35406     groupClass : "x-menu-group-item",
35407
35408     /**
35409      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35410      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35411      * initialized with checked = true will be rendered as checked.
35412      */
35413     checked: false,
35414
35415     // private
35416     ctype: "Roo.menu.CheckItem",
35417
35418     // private
35419     onRender : function(c){
35420         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35421         if(this.group){
35422             this.el.addClass(this.groupClass);
35423         }
35424         Roo.menu.MenuMgr.registerCheckable(this);
35425         if(this.checked){
35426             this.checked = false;
35427             this.setChecked(true, true);
35428         }
35429     },
35430
35431     // private
35432     destroy : function(){
35433         if(this.rendered){
35434             Roo.menu.MenuMgr.unregisterCheckable(this);
35435         }
35436         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35437     },
35438
35439     /**
35440      * Set the checked state of this item
35441      * @param {Boolean} checked The new checked value
35442      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35443      */
35444     setChecked : function(state, suppressEvent){
35445         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35446             if(this.container){
35447                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35448             }
35449             this.checked = state;
35450             if(suppressEvent !== true){
35451                 this.fireEvent("checkchange", this, state);
35452             }
35453         }
35454     },
35455
35456     // private
35457     handleClick : function(e){
35458        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35459            this.setChecked(!this.checked);
35460        }
35461        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35462     }
35463 });/*
35464  * Based on:
35465  * Ext JS Library 1.1.1
35466  * Copyright(c) 2006-2007, Ext JS, LLC.
35467  *
35468  * Originally Released Under LGPL - original licence link has changed is not relivant.
35469  *
35470  * Fork - LGPL
35471  * <script type="text/javascript">
35472  */
35473  
35474 /**
35475  * @class Roo.menu.DateItem
35476  * @extends Roo.menu.Adapter
35477  * A menu item that wraps the {@link Roo.DatPicker} component.
35478  * @constructor
35479  * Creates a new DateItem
35480  * @param {Object} config Configuration options
35481  */
35482 Roo.menu.DateItem = function(config){
35483     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35484     /** The Roo.DatePicker object @type Roo.DatePicker */
35485     this.picker = this.component;
35486     this.addEvents({select: true});
35487     
35488     this.picker.on("render", function(picker){
35489         picker.getEl().swallowEvent("click");
35490         picker.container.addClass("x-menu-date-item");
35491     });
35492
35493     this.picker.on("select", this.onSelect, this);
35494 };
35495
35496 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35497     // private
35498     onSelect : function(picker, date){
35499         this.fireEvent("select", this, date, picker);
35500         Roo.menu.DateItem.superclass.handleClick.call(this);
35501     }
35502 });/*
35503  * Based on:
35504  * Ext JS Library 1.1.1
35505  * Copyright(c) 2006-2007, Ext JS, LLC.
35506  *
35507  * Originally Released Under LGPL - original licence link has changed is not relivant.
35508  *
35509  * Fork - LGPL
35510  * <script type="text/javascript">
35511  */
35512  
35513 /**
35514  * @class Roo.menu.ColorItem
35515  * @extends Roo.menu.Adapter
35516  * A menu item that wraps the {@link Roo.ColorPalette} component.
35517  * @constructor
35518  * Creates a new ColorItem
35519  * @param {Object} config Configuration options
35520  */
35521 Roo.menu.ColorItem = function(config){
35522     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35523     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35524     this.palette = this.component;
35525     this.relayEvents(this.palette, ["select"]);
35526     if(this.selectHandler){
35527         this.on('select', this.selectHandler, this.scope);
35528     }
35529 };
35530 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35531  * Based on:
35532  * Ext JS Library 1.1.1
35533  * Copyright(c) 2006-2007, Ext JS, LLC.
35534  *
35535  * Originally Released Under LGPL - original licence link has changed is not relivant.
35536  *
35537  * Fork - LGPL
35538  * <script type="text/javascript">
35539  */
35540  
35541
35542 /**
35543  * @class Roo.menu.DateMenu
35544  * @extends Roo.menu.Menu
35545  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35546  * @constructor
35547  * Creates a new DateMenu
35548  * @param {Object} config Configuration options
35549  */
35550 Roo.menu.DateMenu = function(config){
35551     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35552     this.plain = true;
35553     var di = new Roo.menu.DateItem(config);
35554     this.add(di);
35555     /**
35556      * The {@link Roo.DatePicker} instance for this DateMenu
35557      * @type DatePicker
35558      */
35559     this.picker = di.picker;
35560     /**
35561      * @event select
35562      * @param {DatePicker} picker
35563      * @param {Date} date
35564      */
35565     this.relayEvents(di, ["select"]);
35566
35567     this.on('beforeshow', function(){
35568         if(this.picker){
35569             this.picker.hideMonthPicker(true);
35570         }
35571     }, this);
35572 };
35573 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35574     cls:'x-date-menu'
35575 });/*
35576  * Based on:
35577  * Ext JS Library 1.1.1
35578  * Copyright(c) 2006-2007, Ext JS, LLC.
35579  *
35580  * Originally Released Under LGPL - original licence link has changed is not relivant.
35581  *
35582  * Fork - LGPL
35583  * <script type="text/javascript">
35584  */
35585  
35586
35587 /**
35588  * @class Roo.menu.ColorMenu
35589  * @extends Roo.menu.Menu
35590  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35591  * @constructor
35592  * Creates a new ColorMenu
35593  * @param {Object} config Configuration options
35594  */
35595 Roo.menu.ColorMenu = function(config){
35596     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35597     this.plain = true;
35598     var ci = new Roo.menu.ColorItem(config);
35599     this.add(ci);
35600     /**
35601      * The {@link Roo.ColorPalette} instance for this ColorMenu
35602      * @type ColorPalette
35603      */
35604     this.palette = ci.palette;
35605     /**
35606      * @event select
35607      * @param {ColorPalette} palette
35608      * @param {String} color
35609      */
35610     this.relayEvents(ci, ["select"]);
35611 };
35612 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35613  * Based on:
35614  * Ext JS Library 1.1.1
35615  * Copyright(c) 2006-2007, Ext JS, LLC.
35616  *
35617  * Originally Released Under LGPL - original licence link has changed is not relivant.
35618  *
35619  * Fork - LGPL
35620  * <script type="text/javascript">
35621  */
35622  
35623 /**
35624  * @class Roo.form.Field
35625  * @extends Roo.BoxComponent
35626  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35627  * @constructor
35628  * Creates a new Field
35629  * @param {Object} config Configuration options
35630  */
35631 Roo.form.Field = function(config){
35632     Roo.form.Field.superclass.constructor.call(this, config);
35633 };
35634
35635 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35636     /**
35637      * @cfg {String} fieldLabel Label to use when rendering a form.
35638      */
35639        /**
35640      * @cfg {String} qtip Mouse over tip
35641      */
35642      
35643     /**
35644      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35645      */
35646     invalidClass : "x-form-invalid",
35647     /**
35648      * @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")
35649      */
35650     invalidText : "The value in this field is invalid",
35651     /**
35652      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35653      */
35654     focusClass : "x-form-focus",
35655     /**
35656      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35657       automatic validation (defaults to "keyup").
35658      */
35659     validationEvent : "keyup",
35660     /**
35661      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35662      */
35663     validateOnBlur : true,
35664     /**
35665      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35666      */
35667     validationDelay : 250,
35668     /**
35669      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35670      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35671      */
35672     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35673     /**
35674      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35675      */
35676     fieldClass : "x-form-field",
35677     /**
35678      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35679      *<pre>
35680 Value         Description
35681 -----------   ----------------------------------------------------------------------
35682 qtip          Display a quick tip when the user hovers over the field
35683 title         Display a default browser title attribute popup
35684 under         Add a block div beneath the field containing the error text
35685 side          Add an error icon to the right of the field with a popup on hover
35686 [element id]  Add the error text directly to the innerHTML of the specified element
35687 </pre>
35688      */
35689     msgTarget : 'qtip',
35690     /**
35691      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35692      */
35693     msgFx : 'normal',
35694
35695     /**
35696      * @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.
35697      */
35698     readOnly : false,
35699
35700     /**
35701      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35702      */
35703     disabled : false,
35704
35705     /**
35706      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35707      */
35708     inputType : undefined,
35709     
35710     /**
35711      * @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).
35712          */
35713         tabIndex : undefined,
35714         
35715     // private
35716     isFormField : true,
35717
35718     // private
35719     hasFocus : false,
35720     /**
35721      * @property {Roo.Element} fieldEl
35722      * Element Containing the rendered Field (with label etc.)
35723      */
35724     /**
35725      * @cfg {Mixed} value A value to initialize this field with.
35726      */
35727     value : undefined,
35728
35729     /**
35730      * @cfg {String} name The field's HTML name attribute.
35731      */
35732     /**
35733      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35734      */
35735
35736         // private ??
35737         initComponent : function(){
35738         Roo.form.Field.superclass.initComponent.call(this);
35739         this.addEvents({
35740             /**
35741              * @event focus
35742              * Fires when this field receives input focus.
35743              * @param {Roo.form.Field} this
35744              */
35745             focus : true,
35746             /**
35747              * @event blur
35748              * Fires when this field loses input focus.
35749              * @param {Roo.form.Field} this
35750              */
35751             blur : true,
35752             /**
35753              * @event specialkey
35754              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35755              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35756              * @param {Roo.form.Field} this
35757              * @param {Roo.EventObject} e The event object
35758              */
35759             specialkey : true,
35760             /**
35761              * @event change
35762              * Fires just before the field blurs if the field value has changed.
35763              * @param {Roo.form.Field} this
35764              * @param {Mixed} newValue The new value
35765              * @param {Mixed} oldValue The original value
35766              */
35767             change : true,
35768             /**
35769              * @event invalid
35770              * Fires after the field has been marked as invalid.
35771              * @param {Roo.form.Field} this
35772              * @param {String} msg The validation message
35773              */
35774             invalid : true,
35775             /**
35776              * @event valid
35777              * Fires after the field has been validated with no errors.
35778              * @param {Roo.form.Field} this
35779              */
35780             valid : true,
35781              /**
35782              * @event keyup
35783              * Fires after the key up
35784              * @param {Roo.form.Field} this
35785              * @param {Roo.EventObject}  e The event Object
35786              */
35787             keyup : true
35788         });
35789     },
35790
35791     /**
35792      * Returns the name attribute of the field if available
35793      * @return {String} name The field name
35794      */
35795     getName: function(){
35796          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35797     },
35798
35799     // private
35800     onRender : function(ct, position){
35801         Roo.form.Field.superclass.onRender.call(this, ct, position);
35802         if(!this.el){
35803             var cfg = this.getAutoCreate();
35804             if(!cfg.name){
35805                 cfg.name = this.name || this.id;
35806             }
35807             if(this.inputType){
35808                 cfg.type = this.inputType;
35809             }
35810             this.el = ct.createChild(cfg, position);
35811         }
35812         var type = this.el.dom.type;
35813         if(type){
35814             if(type == 'password'){
35815                 type = 'text';
35816             }
35817             this.el.addClass('x-form-'+type);
35818         }
35819         if(this.readOnly){
35820             this.el.dom.readOnly = true;
35821         }
35822         if(this.tabIndex !== undefined){
35823             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35824         }
35825
35826         this.el.addClass([this.fieldClass, this.cls]);
35827         this.initValue();
35828     },
35829
35830     /**
35831      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35832      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35833      * @return {Roo.form.Field} this
35834      */
35835     applyTo : function(target){
35836         this.allowDomMove = false;
35837         this.el = Roo.get(target);
35838         this.render(this.el.dom.parentNode);
35839         return this;
35840     },
35841
35842     // private
35843     initValue : function(){
35844         if(this.value !== undefined){
35845             this.setValue(this.value);
35846         }else if(this.el.dom.value.length > 0){
35847             this.setValue(this.el.dom.value);
35848         }
35849     },
35850
35851     /**
35852      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35853      */
35854     isDirty : function() {
35855         if(this.disabled) {
35856             return false;
35857         }
35858         return String(this.getValue()) !== String(this.originalValue);
35859     },
35860
35861     // private
35862     afterRender : function(){
35863         Roo.form.Field.superclass.afterRender.call(this);
35864         this.initEvents();
35865     },
35866
35867     // private
35868     fireKey : function(e){
35869         //Roo.log('field ' + e.getKey());
35870         if(e.isNavKeyPress()){
35871             this.fireEvent("specialkey", this, e);
35872         }
35873     },
35874
35875     /**
35876      * Resets the current field value to the originally loaded value and clears any validation messages
35877      */
35878     reset : function(){
35879         this.setValue(this.originalValue);
35880         this.clearInvalid();
35881     },
35882
35883     // private
35884     initEvents : function(){
35885         // safari killled keypress - so keydown is now used..
35886         this.el.on("keydown" , this.fireKey,  this);
35887         this.el.on("focus", this.onFocus,  this);
35888         this.el.on("blur", this.onBlur,  this);
35889         this.el.relayEvent('keyup', this);
35890
35891         // reference to original value for reset
35892         this.originalValue = this.getValue();
35893     },
35894
35895     // private
35896     onFocus : function(){
35897         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35898             this.el.addClass(this.focusClass);
35899         }
35900         if(!this.hasFocus){
35901             this.hasFocus = true;
35902             this.startValue = this.getValue();
35903             this.fireEvent("focus", this);
35904         }
35905     },
35906
35907     beforeBlur : Roo.emptyFn,
35908
35909     // private
35910     onBlur : function(){
35911         this.beforeBlur();
35912         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35913             this.el.removeClass(this.focusClass);
35914         }
35915         this.hasFocus = false;
35916         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35917             this.validate();
35918         }
35919         var v = this.getValue();
35920         if(String(v) !== String(this.startValue)){
35921             this.fireEvent('change', this, v, this.startValue);
35922         }
35923         this.fireEvent("blur", this);
35924     },
35925
35926     /**
35927      * Returns whether or not the field value is currently valid
35928      * @param {Boolean} preventMark True to disable marking the field invalid
35929      * @return {Boolean} True if the value is valid, else false
35930      */
35931     isValid : function(preventMark){
35932         if(this.disabled){
35933             return true;
35934         }
35935         var restore = this.preventMark;
35936         this.preventMark = preventMark === true;
35937         var v = this.validateValue(this.processValue(this.getRawValue()));
35938         this.preventMark = restore;
35939         return v;
35940     },
35941
35942     /**
35943      * Validates the field value
35944      * @return {Boolean} True if the value is valid, else false
35945      */
35946     validate : function(){
35947         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35948             this.clearInvalid();
35949             return true;
35950         }
35951         return false;
35952     },
35953
35954     processValue : function(value){
35955         return value;
35956     },
35957
35958     // private
35959     // Subclasses should provide the validation implementation by overriding this
35960     validateValue : function(value){
35961         return true;
35962     },
35963
35964     /**
35965      * Mark this field as invalid
35966      * @param {String} msg The validation message
35967      */
35968     markInvalid : function(msg){
35969         if(!this.rendered || this.preventMark){ // not rendered
35970             return;
35971         }
35972         this.el.addClass(this.invalidClass);
35973         msg = msg || this.invalidText;
35974         switch(this.msgTarget){
35975             case 'qtip':
35976                 this.el.dom.qtip = msg;
35977                 this.el.dom.qclass = 'x-form-invalid-tip';
35978                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35979                     Roo.QuickTips.enable();
35980                 }
35981                 break;
35982             case 'title':
35983                 this.el.dom.title = msg;
35984                 break;
35985             case 'under':
35986                 if(!this.errorEl){
35987                     var elp = this.el.findParent('.x-form-element', 5, true);
35988                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35989                     this.errorEl.setWidth(elp.getWidth(true)-20);
35990                 }
35991                 this.errorEl.update(msg);
35992                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35993                 break;
35994             case 'side':
35995                 if(!this.errorIcon){
35996                     var elp = this.el.findParent('.x-form-element', 5, true);
35997                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35998                 }
35999                 this.alignErrorIcon();
36000                 this.errorIcon.dom.qtip = msg;
36001                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36002                 this.errorIcon.show();
36003                 this.on('resize', this.alignErrorIcon, this);
36004                 break;
36005             default:
36006                 var t = Roo.getDom(this.msgTarget);
36007                 t.innerHTML = msg;
36008                 t.style.display = this.msgDisplay;
36009                 break;
36010         }
36011         this.fireEvent('invalid', this, msg);
36012     },
36013
36014     // private
36015     alignErrorIcon : function(){
36016         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36017     },
36018
36019     /**
36020      * Clear any invalid styles/messages for this field
36021      */
36022     clearInvalid : function(){
36023         if(!this.rendered || this.preventMark){ // not rendered
36024             return;
36025         }
36026         this.el.removeClass(this.invalidClass);
36027         switch(this.msgTarget){
36028             case 'qtip':
36029                 this.el.dom.qtip = '';
36030                 break;
36031             case 'title':
36032                 this.el.dom.title = '';
36033                 break;
36034             case 'under':
36035                 if(this.errorEl){
36036                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36037                 }
36038                 break;
36039             case 'side':
36040                 if(this.errorIcon){
36041                     this.errorIcon.dom.qtip = '';
36042                     this.errorIcon.hide();
36043                     this.un('resize', this.alignErrorIcon, this);
36044                 }
36045                 break;
36046             default:
36047                 var t = Roo.getDom(this.msgTarget);
36048                 t.innerHTML = '';
36049                 t.style.display = 'none';
36050                 break;
36051         }
36052         this.fireEvent('valid', this);
36053     },
36054
36055     /**
36056      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36057      * @return {Mixed} value The field value
36058      */
36059     getRawValue : function(){
36060         var v = this.el.getValue();
36061         if(v === this.emptyText){
36062             v = '';
36063         }
36064         return v;
36065     },
36066
36067     /**
36068      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36069      * @return {Mixed} value The field value
36070      */
36071     getValue : function(){
36072         var v = this.el.getValue();
36073         if(v === this.emptyText || v === undefined){
36074             v = '';
36075         }
36076         return v;
36077     },
36078
36079     /**
36080      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36081      * @param {Mixed} value The value to set
36082      */
36083     setRawValue : function(v){
36084         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36085     },
36086
36087     /**
36088      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36089      * @param {Mixed} value The value to set
36090      */
36091     setValue : function(v){
36092         this.value = v;
36093         if(this.rendered){
36094             this.el.dom.value = (v === null || v === undefined ? '' : v);
36095              this.validate();
36096         }
36097     },
36098
36099     adjustSize : function(w, h){
36100         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36101         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36102         return s;
36103     },
36104
36105     adjustWidth : function(tag, w){
36106         tag = tag.toLowerCase();
36107         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36108             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36109                 if(tag == 'input'){
36110                     return w + 2;
36111                 }
36112                 if(tag = 'textarea'){
36113                     return w-2;
36114                 }
36115             }else if(Roo.isOpera){
36116                 if(tag == 'input'){
36117                     return w + 2;
36118                 }
36119                 if(tag = 'textarea'){
36120                     return w-2;
36121                 }
36122             }
36123         }
36124         return w;
36125     }
36126 });
36127
36128
36129 // anything other than normal should be considered experimental
36130 Roo.form.Field.msgFx = {
36131     normal : {
36132         show: function(msgEl, f){
36133             msgEl.setDisplayed('block');
36134         },
36135
36136         hide : function(msgEl, f){
36137             msgEl.setDisplayed(false).update('');
36138         }
36139     },
36140
36141     slide : {
36142         show: function(msgEl, f){
36143             msgEl.slideIn('t', {stopFx:true});
36144         },
36145
36146         hide : function(msgEl, f){
36147             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36148         }
36149     },
36150
36151     slideRight : {
36152         show: function(msgEl, f){
36153             msgEl.fixDisplay();
36154             msgEl.alignTo(f.el, 'tl-tr');
36155             msgEl.slideIn('l', {stopFx:true});
36156         },
36157
36158         hide : function(msgEl, f){
36159             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36160         }
36161     }
36162 };/*
36163  * Based on:
36164  * Ext JS Library 1.1.1
36165  * Copyright(c) 2006-2007, Ext JS, LLC.
36166  *
36167  * Originally Released Under LGPL - original licence link has changed is not relivant.
36168  *
36169  * Fork - LGPL
36170  * <script type="text/javascript">
36171  */
36172  
36173
36174 /**
36175  * @class Roo.form.TextField
36176  * @extends Roo.form.Field
36177  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36178  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36179  * @constructor
36180  * Creates a new TextField
36181  * @param {Object} config Configuration options
36182  */
36183 Roo.form.TextField = function(config){
36184     Roo.form.TextField.superclass.constructor.call(this, config);
36185     this.addEvents({
36186         /**
36187          * @event autosize
36188          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36189          * according to the default logic, but this event provides a hook for the developer to apply additional
36190          * logic at runtime to resize the field if needed.
36191              * @param {Roo.form.Field} this This text field
36192              * @param {Number} width The new field width
36193              */
36194         autosize : true
36195     });
36196 };
36197
36198 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36199     /**
36200      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36201      */
36202     grow : false,
36203     /**
36204      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36205      */
36206     growMin : 30,
36207     /**
36208      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36209      */
36210     growMax : 800,
36211     /**
36212      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36213      */
36214     vtype : null,
36215     /**
36216      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36217      */
36218     maskRe : null,
36219     /**
36220      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36221      */
36222     disableKeyFilter : false,
36223     /**
36224      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36225      */
36226     allowBlank : true,
36227     /**
36228      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36229      */
36230     minLength : 0,
36231     /**
36232      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36233      */
36234     maxLength : Number.MAX_VALUE,
36235     /**
36236      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36237      */
36238     minLengthText : "The minimum length for this field is {0}",
36239     /**
36240      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36241      */
36242     maxLengthText : "The maximum length for this field is {0}",
36243     /**
36244      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36245      */
36246     selectOnFocus : false,
36247     /**
36248      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36249      */
36250     blankText : "This field is required",
36251     /**
36252      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36253      * If available, this function will be called only after the basic validators all return true, and will be passed the
36254      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36255      */
36256     validator : null,
36257     /**
36258      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36259      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36260      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36261      */
36262     regex : null,
36263     /**
36264      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36265      */
36266     regexText : "",
36267     /**
36268      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36269      */
36270     emptyText : null,
36271     /**
36272      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36273      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36274      */
36275     emptyClass : 'x-form-empty-field',
36276
36277     // private
36278     initEvents : function(){
36279         Roo.form.TextField.superclass.initEvents.call(this);
36280         if(this.validationEvent == 'keyup'){
36281             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36282             this.el.on('keyup', this.filterValidation, this);
36283         }
36284         else if(this.validationEvent !== false){
36285             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36286         }
36287         if(this.selectOnFocus || this.emptyText){
36288             this.on("focus", this.preFocus, this);
36289             if(this.emptyText){
36290                 this.on('blur', this.postBlur, this);
36291                 this.applyEmptyText();
36292             }
36293         }
36294         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36295             this.el.on("keypress", this.filterKeys, this);
36296         }
36297         if(this.grow){
36298             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36299             this.el.on("click", this.autoSize,  this);
36300         }
36301     },
36302
36303     processValue : function(value){
36304         if(this.stripCharsRe){
36305             var newValue = value.replace(this.stripCharsRe, '');
36306             if(newValue !== value){
36307                 this.setRawValue(newValue);
36308                 return newValue;
36309             }
36310         }
36311         return value;
36312     },
36313
36314     filterValidation : function(e){
36315         if(!e.isNavKeyPress()){
36316             this.validationTask.delay(this.validationDelay);
36317         }
36318     },
36319
36320     // private
36321     onKeyUp : function(e){
36322         if(!e.isNavKeyPress()){
36323             this.autoSize();
36324         }
36325     },
36326
36327     /**
36328      * Resets the current field value to the originally-loaded value and clears any validation messages.
36329      * Also adds emptyText and emptyClass if the original value was blank.
36330      */
36331     reset : function(){
36332         Roo.form.TextField.superclass.reset.call(this);
36333         this.applyEmptyText();
36334     },
36335
36336     applyEmptyText : function(){
36337         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36338             this.setRawValue(this.emptyText);
36339             this.el.addClass(this.emptyClass);
36340         }
36341     },
36342
36343     // private
36344     preFocus : function(){
36345         if(this.emptyText){
36346             if(this.el.dom.value == this.emptyText){
36347                 this.setRawValue('');
36348             }
36349             this.el.removeClass(this.emptyClass);
36350         }
36351         if(this.selectOnFocus){
36352             this.el.dom.select();
36353         }
36354     },
36355
36356     // private
36357     postBlur : function(){
36358         this.applyEmptyText();
36359     },
36360
36361     // private
36362     filterKeys : function(e){
36363         var k = e.getKey();
36364         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36365             return;
36366         }
36367         var c = e.getCharCode(), cc = String.fromCharCode(c);
36368         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36369             return;
36370         }
36371         if(!this.maskRe.test(cc)){
36372             e.stopEvent();
36373         }
36374     },
36375
36376     setValue : function(v){
36377         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36378             this.el.removeClass(this.emptyClass);
36379         }
36380         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36381         this.applyEmptyText();
36382         this.autoSize();
36383     },
36384
36385     /**
36386      * Validates a value according to the field's validation rules and marks the field as invalid
36387      * if the validation fails
36388      * @param {Mixed} value The value to validate
36389      * @return {Boolean} True if the value is valid, else false
36390      */
36391     validateValue : function(value){
36392         if(value.length < 1 || value === this.emptyText){ // if it's blank
36393              if(this.allowBlank){
36394                 this.clearInvalid();
36395                 return true;
36396              }else{
36397                 this.markInvalid(this.blankText);
36398                 return false;
36399              }
36400         }
36401         if(value.length < this.minLength){
36402             this.markInvalid(String.format(this.minLengthText, this.minLength));
36403             return false;
36404         }
36405         if(value.length > this.maxLength){
36406             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36407             return false;
36408         }
36409         if(this.vtype){
36410             var vt = Roo.form.VTypes;
36411             if(!vt[this.vtype](value, this)){
36412                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36413                 return false;
36414             }
36415         }
36416         if(typeof this.validator == "function"){
36417             var msg = this.validator(value);
36418             if(msg !== true){
36419                 this.markInvalid(msg);
36420                 return false;
36421             }
36422         }
36423         if(this.regex && !this.regex.test(value)){
36424             this.markInvalid(this.regexText);
36425             return false;
36426         }
36427         return true;
36428     },
36429
36430     /**
36431      * Selects text in this field
36432      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36433      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36434      */
36435     selectText : function(start, end){
36436         var v = this.getRawValue();
36437         if(v.length > 0){
36438             start = start === undefined ? 0 : start;
36439             end = end === undefined ? v.length : end;
36440             var d = this.el.dom;
36441             if(d.setSelectionRange){
36442                 d.setSelectionRange(start, end);
36443             }else if(d.createTextRange){
36444                 var range = d.createTextRange();
36445                 range.moveStart("character", start);
36446                 range.moveEnd("character", v.length-end);
36447                 range.select();
36448             }
36449         }
36450     },
36451
36452     /**
36453      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36454      * This only takes effect if grow = true, and fires the autosize event.
36455      */
36456     autoSize : function(){
36457         if(!this.grow || !this.rendered){
36458             return;
36459         }
36460         if(!this.metrics){
36461             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36462         }
36463         var el = this.el;
36464         var v = el.dom.value;
36465         var d = document.createElement('div');
36466         d.appendChild(document.createTextNode(v));
36467         v = d.innerHTML;
36468         d = null;
36469         v += "&#160;";
36470         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36471         this.el.setWidth(w);
36472         this.fireEvent("autosize", this, w);
36473     }
36474 });/*
36475  * Based on:
36476  * Ext JS Library 1.1.1
36477  * Copyright(c) 2006-2007, Ext JS, LLC.
36478  *
36479  * Originally Released Under LGPL - original licence link has changed is not relivant.
36480  *
36481  * Fork - LGPL
36482  * <script type="text/javascript">
36483  */
36484  
36485 /**
36486  * @class Roo.form.Hidden
36487  * @extends Roo.form.TextField
36488  * Simple Hidden element used on forms 
36489  * 
36490  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36491  * 
36492  * @constructor
36493  * Creates a new Hidden form element.
36494  * @param {Object} config Configuration options
36495  */
36496
36497
36498
36499 // easy hidden field...
36500 Roo.form.Hidden = function(config){
36501     Roo.form.Hidden.superclass.constructor.call(this, config);
36502 };
36503   
36504 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36505     fieldLabel:      '',
36506     inputType:      'hidden',
36507     width:          50,
36508     allowBlank:     true,
36509     labelSeparator: '',
36510     hidden:         true,
36511     itemCls :       'x-form-item-display-none'
36512
36513
36514 });
36515
36516
36517 /*
36518  * Based on:
36519  * Ext JS Library 1.1.1
36520  * Copyright(c) 2006-2007, Ext JS, LLC.
36521  *
36522  * Originally Released Under LGPL - original licence link has changed is not relivant.
36523  *
36524  * Fork - LGPL
36525  * <script type="text/javascript">
36526  */
36527  
36528 /**
36529  * @class Roo.form.TriggerField
36530  * @extends Roo.form.TextField
36531  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36532  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36533  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36534  * for which you can provide a custom implementation.  For example:
36535  * <pre><code>
36536 var trigger = new Roo.form.TriggerField();
36537 trigger.onTriggerClick = myTriggerFn;
36538 trigger.applyTo('my-field');
36539 </code></pre>
36540  *
36541  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36542  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36543  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36544  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36545  * @constructor
36546  * Create a new TriggerField.
36547  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36548  * to the base TextField)
36549  */
36550 Roo.form.TriggerField = function(config){
36551     this.mimicing = false;
36552     Roo.form.TriggerField.superclass.constructor.call(this, config);
36553 };
36554
36555 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36556     /**
36557      * @cfg {String} triggerClass A CSS class to apply to the trigger
36558      */
36559     /**
36560      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36561      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36562      */
36563     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36564     /**
36565      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36566      */
36567     hideTrigger:false,
36568
36569     /** @cfg {Boolean} grow @hide */
36570     /** @cfg {Number} growMin @hide */
36571     /** @cfg {Number} growMax @hide */
36572
36573     /**
36574      * @hide 
36575      * @method
36576      */
36577     autoSize: Roo.emptyFn,
36578     // private
36579     monitorTab : true,
36580     // private
36581     deferHeight : true,
36582
36583     
36584     actionMode : 'wrap',
36585     // private
36586     onResize : function(w, h){
36587         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36588         if(typeof w == 'number'){
36589             var x = w - this.trigger.getWidth();
36590             this.el.setWidth(this.adjustWidth('input', x));
36591             this.trigger.setStyle('left', x+'px');
36592         }
36593     },
36594
36595     // private
36596     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36597
36598     // private
36599     getResizeEl : function(){
36600         return this.wrap;
36601     },
36602
36603     // private
36604     getPositionEl : function(){
36605         return this.wrap;
36606     },
36607
36608     // private
36609     alignErrorIcon : function(){
36610         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36611     },
36612
36613     // private
36614     onRender : function(ct, position){
36615         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36616         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36617         this.trigger = this.wrap.createChild(this.triggerConfig ||
36618                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36619         if(this.hideTrigger){
36620             this.trigger.setDisplayed(false);
36621         }
36622         this.initTrigger();
36623         if(!this.width){
36624             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36625         }
36626     },
36627
36628     // private
36629     initTrigger : function(){
36630         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36631         this.trigger.addClassOnOver('x-form-trigger-over');
36632         this.trigger.addClassOnClick('x-form-trigger-click');
36633     },
36634
36635     // private
36636     onDestroy : function(){
36637         if(this.trigger){
36638             this.trigger.removeAllListeners();
36639             this.trigger.remove();
36640         }
36641         if(this.wrap){
36642             this.wrap.remove();
36643         }
36644         Roo.form.TriggerField.superclass.onDestroy.call(this);
36645     },
36646
36647     // private
36648     onFocus : function(){
36649         Roo.form.TriggerField.superclass.onFocus.call(this);
36650         if(!this.mimicing){
36651             this.wrap.addClass('x-trigger-wrap-focus');
36652             this.mimicing = true;
36653             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36654             if(this.monitorTab){
36655                 this.el.on("keydown", this.checkTab, this);
36656             }
36657         }
36658     },
36659
36660     // private
36661     checkTab : function(e){
36662         if(e.getKey() == e.TAB){
36663             this.triggerBlur();
36664         }
36665     },
36666
36667     // private
36668     onBlur : function(){
36669         // do nothing
36670     },
36671
36672     // private
36673     mimicBlur : function(e, t){
36674         if(!this.wrap.contains(t) && this.validateBlur()){
36675             this.triggerBlur();
36676         }
36677     },
36678
36679     // private
36680     triggerBlur : function(){
36681         this.mimicing = false;
36682         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36683         if(this.monitorTab){
36684             this.el.un("keydown", this.checkTab, this);
36685         }
36686         this.wrap.removeClass('x-trigger-wrap-focus');
36687         Roo.form.TriggerField.superclass.onBlur.call(this);
36688     },
36689
36690     // private
36691     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36692     validateBlur : function(e, t){
36693         return true;
36694     },
36695
36696     // private
36697     onDisable : function(){
36698         Roo.form.TriggerField.superclass.onDisable.call(this);
36699         if(this.wrap){
36700             this.wrap.addClass('x-item-disabled');
36701         }
36702     },
36703
36704     // private
36705     onEnable : function(){
36706         Roo.form.TriggerField.superclass.onEnable.call(this);
36707         if(this.wrap){
36708             this.wrap.removeClass('x-item-disabled');
36709         }
36710     },
36711
36712     // private
36713     onShow : function(){
36714         var ae = this.getActionEl();
36715         
36716         if(ae){
36717             ae.dom.style.display = '';
36718             ae.dom.style.visibility = 'visible';
36719         }
36720     },
36721
36722     // private
36723     
36724     onHide : function(){
36725         var ae = this.getActionEl();
36726         ae.dom.style.display = 'none';
36727     },
36728
36729     /**
36730      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36731      * by an implementing function.
36732      * @method
36733      * @param {EventObject} e
36734      */
36735     onTriggerClick : Roo.emptyFn
36736 });
36737
36738 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36739 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36740 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36741 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36742     initComponent : function(){
36743         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36744
36745         this.triggerConfig = {
36746             tag:'span', cls:'x-form-twin-triggers', cn:[
36747             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36748             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36749         ]};
36750     },
36751
36752     getTrigger : function(index){
36753         return this.triggers[index];
36754     },
36755
36756     initTrigger : function(){
36757         var ts = this.trigger.select('.x-form-trigger', true);
36758         this.wrap.setStyle('overflow', 'hidden');
36759         var triggerField = this;
36760         ts.each(function(t, all, index){
36761             t.hide = function(){
36762                 var w = triggerField.wrap.getWidth();
36763                 this.dom.style.display = 'none';
36764                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36765             };
36766             t.show = function(){
36767                 var w = triggerField.wrap.getWidth();
36768                 this.dom.style.display = '';
36769                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36770             };
36771             var triggerIndex = 'Trigger'+(index+1);
36772
36773             if(this['hide'+triggerIndex]){
36774                 t.dom.style.display = 'none';
36775             }
36776             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36777             t.addClassOnOver('x-form-trigger-over');
36778             t.addClassOnClick('x-form-trigger-click');
36779         }, this);
36780         this.triggers = ts.elements;
36781     },
36782
36783     onTrigger1Click : Roo.emptyFn,
36784     onTrigger2Click : Roo.emptyFn
36785 });/*
36786  * Based on:
36787  * Ext JS Library 1.1.1
36788  * Copyright(c) 2006-2007, Ext JS, LLC.
36789  *
36790  * Originally Released Under LGPL - original licence link has changed is not relivant.
36791  *
36792  * Fork - LGPL
36793  * <script type="text/javascript">
36794  */
36795  
36796 /**
36797  * @class Roo.form.TextArea
36798  * @extends Roo.form.TextField
36799  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36800  * support for auto-sizing.
36801  * @constructor
36802  * Creates a new TextArea
36803  * @param {Object} config Configuration options
36804  */
36805 Roo.form.TextArea = function(config){
36806     Roo.form.TextArea.superclass.constructor.call(this, config);
36807     // these are provided exchanges for backwards compat
36808     // minHeight/maxHeight were replaced by growMin/growMax to be
36809     // compatible with TextField growing config values
36810     if(this.minHeight !== undefined){
36811         this.growMin = this.minHeight;
36812     }
36813     if(this.maxHeight !== undefined){
36814         this.growMax = this.maxHeight;
36815     }
36816 };
36817
36818 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36819     /**
36820      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36821      */
36822     growMin : 60,
36823     /**
36824      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36825      */
36826     growMax: 1000,
36827     /**
36828      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36829      * in the field (equivalent to setting overflow: hidden, defaults to false)
36830      */
36831     preventScrollbars: false,
36832     /**
36833      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36834      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36835      */
36836
36837     // private
36838     onRender : function(ct, position){
36839         if(!this.el){
36840             this.defaultAutoCreate = {
36841                 tag: "textarea",
36842                 style:"width:300px;height:60px;",
36843                 autocomplete: "off"
36844             };
36845         }
36846         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36847         if(this.grow){
36848             this.textSizeEl = Roo.DomHelper.append(document.body, {
36849                 tag: "pre", cls: "x-form-grow-sizer"
36850             });
36851             if(this.preventScrollbars){
36852                 this.el.setStyle("overflow", "hidden");
36853             }
36854             this.el.setHeight(this.growMin);
36855         }
36856     },
36857
36858     onDestroy : function(){
36859         if(this.textSizeEl){
36860             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36861         }
36862         Roo.form.TextArea.superclass.onDestroy.call(this);
36863     },
36864
36865     // private
36866     onKeyUp : function(e){
36867         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36868             this.autoSize();
36869         }
36870     },
36871
36872     /**
36873      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36874      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36875      */
36876     autoSize : function(){
36877         if(!this.grow || !this.textSizeEl){
36878             return;
36879         }
36880         var el = this.el;
36881         var v = el.dom.value;
36882         var ts = this.textSizeEl;
36883
36884         ts.innerHTML = '';
36885         ts.appendChild(document.createTextNode(v));
36886         v = ts.innerHTML;
36887
36888         Roo.fly(ts).setWidth(this.el.getWidth());
36889         if(v.length < 1){
36890             v = "&#160;&#160;";
36891         }else{
36892             if(Roo.isIE){
36893                 v = v.replace(/\n/g, '<p>&#160;</p>');
36894             }
36895             v += "&#160;\n&#160;";
36896         }
36897         ts.innerHTML = v;
36898         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36899         if(h != this.lastHeight){
36900             this.lastHeight = h;
36901             this.el.setHeight(h);
36902             this.fireEvent("autosize", this, h);
36903         }
36904     }
36905 });/*
36906  * Based on:
36907  * Ext JS Library 1.1.1
36908  * Copyright(c) 2006-2007, Ext JS, LLC.
36909  *
36910  * Originally Released Under LGPL - original licence link has changed is not relivant.
36911  *
36912  * Fork - LGPL
36913  * <script type="text/javascript">
36914  */
36915  
36916
36917 /**
36918  * @class Roo.form.NumberField
36919  * @extends Roo.form.TextField
36920  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36921  * @constructor
36922  * Creates a new NumberField
36923  * @param {Object} config Configuration options
36924  */
36925 Roo.form.NumberField = function(config){
36926     Roo.form.NumberField.superclass.constructor.call(this, config);
36927 };
36928
36929 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36930     /**
36931      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36932      */
36933     fieldClass: "x-form-field x-form-num-field",
36934     /**
36935      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36936      */
36937     allowDecimals : true,
36938     /**
36939      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36940      */
36941     decimalSeparator : ".",
36942     /**
36943      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36944      */
36945     decimalPrecision : 2,
36946     /**
36947      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36948      */
36949     allowNegative : true,
36950     /**
36951      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36952      */
36953     minValue : Number.NEGATIVE_INFINITY,
36954     /**
36955      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36956      */
36957     maxValue : Number.MAX_VALUE,
36958     /**
36959      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36960      */
36961     minText : "The minimum value for this field is {0}",
36962     /**
36963      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36964      */
36965     maxText : "The maximum value for this field is {0}",
36966     /**
36967      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36968      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36969      */
36970     nanText : "{0} is not a valid number",
36971
36972     // private
36973     initEvents : function(){
36974         Roo.form.NumberField.superclass.initEvents.call(this);
36975         var allowed = "0123456789";
36976         if(this.allowDecimals){
36977             allowed += this.decimalSeparator;
36978         }
36979         if(this.allowNegative){
36980             allowed += "-";
36981         }
36982         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36983         var keyPress = function(e){
36984             var k = e.getKey();
36985             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36986                 return;
36987             }
36988             var c = e.getCharCode();
36989             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36990                 e.stopEvent();
36991             }
36992         };
36993         this.el.on("keypress", keyPress, this);
36994     },
36995
36996     // private
36997     validateValue : function(value){
36998         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36999             return false;
37000         }
37001         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37002              return true;
37003         }
37004         var num = this.parseValue(value);
37005         if(isNaN(num)){
37006             this.markInvalid(String.format(this.nanText, value));
37007             return false;
37008         }
37009         if(num < this.minValue){
37010             this.markInvalid(String.format(this.minText, this.minValue));
37011             return false;
37012         }
37013         if(num > this.maxValue){
37014             this.markInvalid(String.format(this.maxText, this.maxValue));
37015             return false;
37016         }
37017         return true;
37018     },
37019
37020     getValue : function(){
37021         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37022     },
37023
37024     // private
37025     parseValue : function(value){
37026         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37027         return isNaN(value) ? '' : value;
37028     },
37029
37030     // private
37031     fixPrecision : function(value){
37032         var nan = isNaN(value);
37033         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37034             return nan ? '' : value;
37035         }
37036         return parseFloat(value).toFixed(this.decimalPrecision);
37037     },
37038
37039     setValue : function(v){
37040         v = this.fixPrecision(v);
37041         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37042     },
37043
37044     // private
37045     decimalPrecisionFcn : function(v){
37046         return Math.floor(v);
37047     },
37048
37049     beforeBlur : function(){
37050         var v = this.parseValue(this.getRawValue());
37051         if(v){
37052             this.setValue(v);
37053         }
37054     }
37055 });/*
37056  * Based on:
37057  * Ext JS Library 1.1.1
37058  * Copyright(c) 2006-2007, Ext JS, LLC.
37059  *
37060  * Originally Released Under LGPL - original licence link has changed is not relivant.
37061  *
37062  * Fork - LGPL
37063  * <script type="text/javascript">
37064  */
37065  
37066 /**
37067  * @class Roo.form.DateField
37068  * @extends Roo.form.TriggerField
37069  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37070 * @constructor
37071 * Create a new DateField
37072 * @param {Object} config
37073  */
37074 Roo.form.DateField = function(config){
37075     Roo.form.DateField.superclass.constructor.call(this, config);
37076     
37077       this.addEvents({
37078          
37079         /**
37080          * @event select
37081          * Fires when a date is selected
37082              * @param {Roo.form.DateField} combo This combo box
37083              * @param {Date} date The date selected
37084              */
37085         'select' : true
37086          
37087     });
37088     
37089     
37090     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37091     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37092     this.ddMatch = null;
37093     if(this.disabledDates){
37094         var dd = this.disabledDates;
37095         var re = "(?:";
37096         for(var i = 0; i < dd.length; i++){
37097             re += dd[i];
37098             if(i != dd.length-1) re += "|";
37099         }
37100         this.ddMatch = new RegExp(re + ")");
37101     }
37102 };
37103
37104 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37105     /**
37106      * @cfg {String} format
37107      * The default date format string which can be overriden for localization support.  The format must be
37108      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37109      */
37110     format : "m/d/y",
37111     /**
37112      * @cfg {String} altFormats
37113      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37114      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37115      */
37116     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37117     /**
37118      * @cfg {Array} disabledDays
37119      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37120      */
37121     disabledDays : null,
37122     /**
37123      * @cfg {String} disabledDaysText
37124      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37125      */
37126     disabledDaysText : "Disabled",
37127     /**
37128      * @cfg {Array} disabledDates
37129      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37130      * expression so they are very powerful. Some examples:
37131      * <ul>
37132      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37133      * <li>["03/08", "09/16"] would disable those days for every year</li>
37134      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37135      * <li>["03/../2006"] would disable every day in March 2006</li>
37136      * <li>["^03"] would disable every day in every March</li>
37137      * </ul>
37138      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37139      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37140      */
37141     disabledDates : null,
37142     /**
37143      * @cfg {String} disabledDatesText
37144      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37145      */
37146     disabledDatesText : "Disabled",
37147     /**
37148      * @cfg {Date/String} minValue
37149      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37150      * valid format (defaults to null).
37151      */
37152     minValue : null,
37153     /**
37154      * @cfg {Date/String} maxValue
37155      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37156      * valid format (defaults to null).
37157      */
37158     maxValue : null,
37159     /**
37160      * @cfg {String} minText
37161      * The error text to display when the date in the cell is before minValue (defaults to
37162      * 'The date in this field must be after {minValue}').
37163      */
37164     minText : "The date in this field must be equal to or after {0}",
37165     /**
37166      * @cfg {String} maxText
37167      * The error text to display when the date in the cell is after maxValue (defaults to
37168      * 'The date in this field must be before {maxValue}').
37169      */
37170     maxText : "The date in this field must be equal to or before {0}",
37171     /**
37172      * @cfg {String} invalidText
37173      * The error text to display when the date in the field is invalid (defaults to
37174      * '{value} is not a valid date - it must be in the format {format}').
37175      */
37176     invalidText : "{0} is not a valid date - it must be in the format {1}",
37177     /**
37178      * @cfg {String} triggerClass
37179      * An additional CSS class used to style the trigger button.  The trigger will always get the
37180      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37181      * which displays a calendar icon).
37182      */
37183     triggerClass : 'x-form-date-trigger',
37184     
37185
37186     /**
37187      * @cfg {bool} useIso
37188      * if enabled, then the date field will use a hidden field to store the 
37189      * real value as iso formated date. default (false)
37190      */ 
37191     useIso : false,
37192     /**
37193      * @cfg {String/Object} autoCreate
37194      * A DomHelper element spec, or true for a default element spec (defaults to
37195      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37196      */ 
37197     // private
37198     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37199     
37200     // private
37201     hiddenField: false,
37202     
37203     onRender : function(ct, position)
37204     {
37205         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37206         if (this.useIso) {
37207             this.el.dom.removeAttribute('name'); 
37208             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37209                     'before', true);
37210             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37211             // prevent input submission
37212             this.hiddenName = this.name;
37213         }
37214             
37215             
37216     },
37217     
37218     // private
37219     validateValue : function(value)
37220     {
37221         value = this.formatDate(value);
37222         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37223             return false;
37224         }
37225         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37226              return true;
37227         }
37228         var svalue = value;
37229         value = this.parseDate(value);
37230         if(!value){
37231             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37232             return false;
37233         }
37234         var time = value.getTime();
37235         if(this.minValue && time < this.minValue.getTime()){
37236             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37237             return false;
37238         }
37239         if(this.maxValue && time > this.maxValue.getTime()){
37240             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37241             return false;
37242         }
37243         if(this.disabledDays){
37244             var day = value.getDay();
37245             for(var i = 0; i < this.disabledDays.length; i++) {
37246                 if(day === this.disabledDays[i]){
37247                     this.markInvalid(this.disabledDaysText);
37248                     return false;
37249                 }
37250             }
37251         }
37252         var fvalue = this.formatDate(value);
37253         if(this.ddMatch && this.ddMatch.test(fvalue)){
37254             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37255             return false;
37256         }
37257         return true;
37258     },
37259
37260     // private
37261     // Provides logic to override the default TriggerField.validateBlur which just returns true
37262     validateBlur : function(){
37263         return !this.menu || !this.menu.isVisible();
37264     },
37265
37266     /**
37267      * Returns the current date value of the date field.
37268      * @return {Date} The date value
37269      */
37270     getValue : function(){
37271         
37272         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37273     },
37274
37275     /**
37276      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37277      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37278      * (the default format used is "m/d/y").
37279      * <br />Usage:
37280      * <pre><code>
37281 //All of these calls set the same date value (May 4, 2006)
37282
37283 //Pass a date object:
37284 var dt = new Date('5/4/06');
37285 dateField.setValue(dt);
37286
37287 //Pass a date string (default format):
37288 dateField.setValue('5/4/06');
37289
37290 //Pass a date string (custom format):
37291 dateField.format = 'Y-m-d';
37292 dateField.setValue('2006-5-4');
37293 </code></pre>
37294      * @param {String/Date} date The date or valid date string
37295      */
37296     setValue : function(date){
37297         if (this.hiddenField) {
37298             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37299         }
37300         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37301     },
37302
37303     // private
37304     parseDate : function(value){
37305         if(!value || value instanceof Date){
37306             return value;
37307         }
37308         var v = Date.parseDate(value, this.format);
37309         if(!v && this.altFormats){
37310             if(!this.altFormatsArray){
37311                 this.altFormatsArray = this.altFormats.split("|");
37312             }
37313             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37314                 v = Date.parseDate(value, this.altFormatsArray[i]);
37315             }
37316         }
37317         return v;
37318     },
37319
37320     // private
37321     formatDate : function(date, fmt){
37322         return (!date || !(date instanceof Date)) ?
37323                date : date.dateFormat(fmt || this.format);
37324     },
37325
37326     // private
37327     menuListeners : {
37328         select: function(m, d){
37329             this.setValue(d);
37330             this.fireEvent('select', this, d);
37331         },
37332         show : function(){ // retain focus styling
37333             this.onFocus();
37334         },
37335         hide : function(){
37336             this.focus.defer(10, this);
37337             var ml = this.menuListeners;
37338             this.menu.un("select", ml.select,  this);
37339             this.menu.un("show", ml.show,  this);
37340             this.menu.un("hide", ml.hide,  this);
37341         }
37342     },
37343
37344     // private
37345     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37346     onTriggerClick : function(){
37347         if(this.disabled){
37348             return;
37349         }
37350         if(this.menu == null){
37351             this.menu = new Roo.menu.DateMenu();
37352         }
37353         Roo.apply(this.menu.picker,  {
37354             showClear: this.allowBlank,
37355             minDate : this.minValue,
37356             maxDate : this.maxValue,
37357             disabledDatesRE : this.ddMatch,
37358             disabledDatesText : this.disabledDatesText,
37359             disabledDays : this.disabledDays,
37360             disabledDaysText : this.disabledDaysText,
37361             format : this.format,
37362             minText : String.format(this.minText, this.formatDate(this.minValue)),
37363             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37364         });
37365         this.menu.on(Roo.apply({}, this.menuListeners, {
37366             scope:this
37367         }));
37368         this.menu.picker.setValue(this.getValue() || new Date());
37369         this.menu.show(this.el, "tl-bl?");
37370     },
37371
37372     beforeBlur : function(){
37373         var v = this.parseDate(this.getRawValue());
37374         if(v){
37375             this.setValue(v);
37376         }
37377     }
37378
37379     /** @cfg {Boolean} grow @hide */
37380     /** @cfg {Number} growMin @hide */
37381     /** @cfg {Number} growMax @hide */
37382     /**
37383      * @hide
37384      * @method autoSize
37385      */
37386 });/*
37387  * Based on:
37388  * Ext JS Library 1.1.1
37389  * Copyright(c) 2006-2007, Ext JS, LLC.
37390  *
37391  * Originally Released Under LGPL - original licence link has changed is not relivant.
37392  *
37393  * Fork - LGPL
37394  * <script type="text/javascript">
37395  */
37396  
37397
37398 /**
37399  * @class Roo.form.ComboBox
37400  * @extends Roo.form.TriggerField
37401  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37402  * @constructor
37403  * Create a new ComboBox.
37404  * @param {Object} config Configuration options
37405  */
37406 Roo.form.ComboBox = function(config){
37407     Roo.form.ComboBox.superclass.constructor.call(this, config);
37408     this.addEvents({
37409         /**
37410          * @event expand
37411          * Fires when the dropdown list is expanded
37412              * @param {Roo.form.ComboBox} combo This combo box
37413              */
37414         'expand' : true,
37415         /**
37416          * @event collapse
37417          * Fires when the dropdown list is collapsed
37418              * @param {Roo.form.ComboBox} combo This combo box
37419              */
37420         'collapse' : true,
37421         /**
37422          * @event beforeselect
37423          * Fires before a list item is selected. Return false to cancel the selection.
37424              * @param {Roo.form.ComboBox} combo This combo box
37425              * @param {Roo.data.Record} record The data record returned from the underlying store
37426              * @param {Number} index The index of the selected item in the dropdown list
37427              */
37428         'beforeselect' : true,
37429         /**
37430          * @event select
37431          * Fires when a list item is selected
37432              * @param {Roo.form.ComboBox} combo This combo box
37433              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37434              * @param {Number} index The index of the selected item in the dropdown list
37435              */
37436         'select' : true,
37437         /**
37438          * @event beforequery
37439          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37440          * The event object passed has these properties:
37441              * @param {Roo.form.ComboBox} combo This combo box
37442              * @param {String} query The query
37443              * @param {Boolean} forceAll true to force "all" query
37444              * @param {Boolean} cancel true to cancel the query
37445              * @param {Object} e The query event object
37446              */
37447         'beforequery': true,
37448          /**
37449          * @event add
37450          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37451              * @param {Roo.form.ComboBox} combo This combo box
37452              */
37453         'add' : true,
37454         /**
37455          * @event edit
37456          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37457              * @param {Roo.form.ComboBox} combo This combo box
37458              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37459              */
37460         'edit' : true
37461         
37462         
37463     });
37464     if(this.transform){
37465         this.allowDomMove = false;
37466         var s = Roo.getDom(this.transform);
37467         if(!this.hiddenName){
37468             this.hiddenName = s.name;
37469         }
37470         if(!this.store){
37471             this.mode = 'local';
37472             var d = [], opts = s.options;
37473             for(var i = 0, len = opts.length;i < len; i++){
37474                 var o = opts[i];
37475                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37476                 if(o.selected) {
37477                     this.value = value;
37478                 }
37479                 d.push([value, o.text]);
37480             }
37481             this.store = new Roo.data.SimpleStore({
37482                 'id': 0,
37483                 fields: ['value', 'text'],
37484                 data : d
37485             });
37486             this.valueField = 'value';
37487             this.displayField = 'text';
37488         }
37489         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37490         if(!this.lazyRender){
37491             this.target = true;
37492             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37493             s.parentNode.removeChild(s); // remove it
37494             this.render(this.el.parentNode);
37495         }else{
37496             s.parentNode.removeChild(s); // remove it
37497         }
37498
37499     }
37500     if (this.store) {
37501         this.store = Roo.factory(this.store, Roo.data);
37502     }
37503     
37504     this.selectedIndex = -1;
37505     if(this.mode == 'local'){
37506         if(config.queryDelay === undefined){
37507             this.queryDelay = 10;
37508         }
37509         if(config.minChars === undefined){
37510             this.minChars = 0;
37511         }
37512     }
37513 };
37514
37515 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37516     /**
37517      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37518      */
37519     /**
37520      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37521      * rendering into an Roo.Editor, defaults to false)
37522      */
37523     /**
37524      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37525      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37526      */
37527     /**
37528      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37529      */
37530     /**
37531      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37532      * the dropdown list (defaults to undefined, with no header element)
37533      */
37534
37535      /**
37536      * @cfg {String/Roo.Template} tpl The template to use to render the output
37537      */
37538      
37539     // private
37540     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37541     /**
37542      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37543      */
37544     listWidth: undefined,
37545     /**
37546      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37547      * mode = 'remote' or 'text' if mode = 'local')
37548      */
37549     displayField: undefined,
37550     /**
37551      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37552      * mode = 'remote' or 'value' if mode = 'local'). 
37553      * Note: use of a valueField requires the user make a selection
37554      * in order for a value to be mapped.
37555      */
37556     valueField: undefined,
37557     
37558     
37559     /**
37560      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37561      * field's data value (defaults to the underlying DOM element's name)
37562      */
37563     hiddenName: undefined,
37564     /**
37565      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37566      */
37567     listClass: '',
37568     /**
37569      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37570      */
37571     selectedClass: 'x-combo-selected',
37572     /**
37573      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37574      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37575      * which displays a downward arrow icon).
37576      */
37577     triggerClass : 'x-form-arrow-trigger',
37578     /**
37579      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37580      */
37581     shadow:'sides',
37582     /**
37583      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37584      * anchor positions (defaults to 'tl-bl')
37585      */
37586     listAlign: 'tl-bl?',
37587     /**
37588      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37589      */
37590     maxHeight: 300,
37591     /**
37592      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37593      * query specified by the allQuery config option (defaults to 'query')
37594      */
37595     triggerAction: 'query',
37596     /**
37597      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37598      * (defaults to 4, does not apply if editable = false)
37599      */
37600     minChars : 4,
37601     /**
37602      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37603      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37604      */
37605     typeAhead: false,
37606     /**
37607      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37608      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37609      */
37610     queryDelay: 500,
37611     /**
37612      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37613      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37614      */
37615     pageSize: 0,
37616     /**
37617      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37618      * when editable = true (defaults to false)
37619      */
37620     selectOnFocus:false,
37621     /**
37622      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37623      */
37624     queryParam: 'query',
37625     /**
37626      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37627      * when mode = 'remote' (defaults to 'Loading...')
37628      */
37629     loadingText: 'Loading...',
37630     /**
37631      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37632      */
37633     resizable: false,
37634     /**
37635      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37636      */
37637     handleHeight : 8,
37638     /**
37639      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37640      * traditional select (defaults to true)
37641      */
37642     editable: true,
37643     /**
37644      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37645      */
37646     allQuery: '',
37647     /**
37648      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37649      */
37650     mode: 'remote',
37651     /**
37652      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37653      * listWidth has a higher value)
37654      */
37655     minListWidth : 70,
37656     /**
37657      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37658      * allow the user to set arbitrary text into the field (defaults to false)
37659      */
37660     forceSelection:false,
37661     /**
37662      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37663      * if typeAhead = true (defaults to 250)
37664      */
37665     typeAheadDelay : 250,
37666     /**
37667      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37668      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37669      */
37670     valueNotFoundText : undefined,
37671     /**
37672      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37673      */
37674     blockFocus : false,
37675     
37676     /**
37677      * @cfg {Boolean} disableClear Disable showing of clear button.
37678      */
37679     disableClear : false,
37680     /**
37681      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37682      */
37683     alwaysQuery : false,
37684     
37685     //private
37686     addicon : false,
37687     editicon: false,
37688     
37689     // element that contains real text value.. (when hidden is used..)
37690      
37691     // private
37692     onRender : function(ct, position){
37693         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37694         if(this.hiddenName){
37695             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37696                     'before', true);
37697             this.hiddenField.value =
37698                 this.hiddenValue !== undefined ? this.hiddenValue :
37699                 this.value !== undefined ? this.value : '';
37700
37701             // prevent input submission
37702             this.el.dom.removeAttribute('name');
37703              
37704              
37705         }
37706         if(Roo.isGecko){
37707             this.el.dom.setAttribute('autocomplete', 'off');
37708         }
37709
37710         var cls = 'x-combo-list';
37711
37712         this.list = new Roo.Layer({
37713             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37714         });
37715
37716         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37717         this.list.setWidth(lw);
37718         this.list.swallowEvent('mousewheel');
37719         this.assetHeight = 0;
37720
37721         if(this.title){
37722             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37723             this.assetHeight += this.header.getHeight();
37724         }
37725
37726         this.innerList = this.list.createChild({cls:cls+'-inner'});
37727         this.innerList.on('mouseover', this.onViewOver, this);
37728         this.innerList.on('mousemove', this.onViewMove, this);
37729         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37730         
37731         if(this.allowBlank && !this.pageSize && !this.disableClear){
37732             this.footer = this.list.createChild({cls:cls+'-ft'});
37733             this.pageTb = new Roo.Toolbar(this.footer);
37734            
37735         }
37736         if(this.pageSize){
37737             this.footer = this.list.createChild({cls:cls+'-ft'});
37738             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37739                     {pageSize: this.pageSize});
37740             
37741         }
37742         
37743         if (this.pageTb && this.allowBlank && !this.disableClear) {
37744             var _this = this;
37745             this.pageTb.add(new Roo.Toolbar.Fill(), {
37746                 cls: 'x-btn-icon x-btn-clear',
37747                 text: '&#160;',
37748                 handler: function()
37749                 {
37750                     _this.collapse();
37751                     _this.clearValue();
37752                     _this.onSelect(false, -1);
37753                 }
37754             });
37755         }
37756         if (this.footer) {
37757             this.assetHeight += this.footer.getHeight();
37758         }
37759         
37760
37761         if(!this.tpl){
37762             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37763         }
37764
37765         this.view = new Roo.View(this.innerList, this.tpl, {
37766             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37767         });
37768
37769         this.view.on('click', this.onViewClick, this);
37770
37771         this.store.on('beforeload', this.onBeforeLoad, this);
37772         this.store.on('load', this.onLoad, this);
37773         this.store.on('loadexception', this.onLoadException, this);
37774
37775         if(this.resizable){
37776             this.resizer = new Roo.Resizable(this.list,  {
37777                pinned:true, handles:'se'
37778             });
37779             this.resizer.on('resize', function(r, w, h){
37780                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37781                 this.listWidth = w;
37782                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37783                 this.restrictHeight();
37784             }, this);
37785             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37786         }
37787         if(!this.editable){
37788             this.editable = true;
37789             this.setEditable(false);
37790         }  
37791         
37792         
37793         if (typeof(this.events.add.listeners) != 'undefined') {
37794             
37795             this.addicon = this.wrap.createChild(
37796                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37797        
37798             this.addicon.on('click', function(e) {
37799                 this.fireEvent('add', this);
37800             }, this);
37801         }
37802         if (typeof(this.events.edit.listeners) != 'undefined') {
37803             
37804             this.editicon = this.wrap.createChild(
37805                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37806             if (this.addicon) {
37807                 this.editicon.setStyle('margin-left', '40px');
37808             }
37809             this.editicon.on('click', function(e) {
37810                 
37811                 // we fire even  if inothing is selected..
37812                 this.fireEvent('edit', this, this.lastData );
37813                 
37814             }, this);
37815         }
37816         
37817         
37818         
37819     },
37820
37821     // private
37822     initEvents : function(){
37823         Roo.form.ComboBox.superclass.initEvents.call(this);
37824
37825         this.keyNav = new Roo.KeyNav(this.el, {
37826             "up" : function(e){
37827                 this.inKeyMode = true;
37828                 this.selectPrev();
37829             },
37830
37831             "down" : function(e){
37832                 if(!this.isExpanded()){
37833                     this.onTriggerClick();
37834                 }else{
37835                     this.inKeyMode = true;
37836                     this.selectNext();
37837                 }
37838             },
37839
37840             "enter" : function(e){
37841                 this.onViewClick();
37842                 //return true;
37843             },
37844
37845             "esc" : function(e){
37846                 this.collapse();
37847             },
37848
37849             "tab" : function(e){
37850                 this.onViewClick(false);
37851                 this.fireEvent("specialkey", this, e);
37852                 return true;
37853             },
37854
37855             scope : this,
37856
37857             doRelay : function(foo, bar, hname){
37858                 if(hname == 'down' || this.scope.isExpanded()){
37859                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37860                 }
37861                 return true;
37862             },
37863
37864             forceKeyDown: true
37865         });
37866         this.queryDelay = Math.max(this.queryDelay || 10,
37867                 this.mode == 'local' ? 10 : 250);
37868         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37869         if(this.typeAhead){
37870             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37871         }
37872         if(this.editable !== false){
37873             this.el.on("keyup", this.onKeyUp, this);
37874         }
37875         if(this.forceSelection){
37876             this.on('blur', this.doForce, this);
37877         }
37878     },
37879
37880     onDestroy : function(){
37881         if(this.view){
37882             this.view.setStore(null);
37883             this.view.el.removeAllListeners();
37884             this.view.el.remove();
37885             this.view.purgeListeners();
37886         }
37887         if(this.list){
37888             this.list.destroy();
37889         }
37890         if(this.store){
37891             this.store.un('beforeload', this.onBeforeLoad, this);
37892             this.store.un('load', this.onLoad, this);
37893             this.store.un('loadexception', this.onLoadException, this);
37894         }
37895         Roo.form.ComboBox.superclass.onDestroy.call(this);
37896     },
37897
37898     // private
37899     fireKey : function(e){
37900         if(e.isNavKeyPress() && !this.list.isVisible()){
37901             this.fireEvent("specialkey", this, e);
37902         }
37903     },
37904
37905     // private
37906     onResize: function(w, h){
37907         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37908         
37909         if(typeof w != 'number'){
37910             // we do not handle it!?!?
37911             return;
37912         }
37913         var tw = this.trigger.getWidth();
37914         tw += this.addicon ? this.addicon.getWidth() : 0;
37915         tw += this.editicon ? this.editicon.getWidth() : 0;
37916         var x = w - tw;
37917         this.el.setWidth( this.adjustWidth('input', x));
37918             
37919         this.trigger.setStyle('left', x+'px');
37920         
37921         if(this.list && this.listWidth === undefined){
37922             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37923             this.list.setWidth(lw);
37924             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37925         }
37926         
37927     
37928         
37929     },
37930
37931     /**
37932      * Allow or prevent the user from directly editing the field text.  If false is passed,
37933      * the user will only be able to select from the items defined in the dropdown list.  This method
37934      * is the runtime equivalent of setting the 'editable' config option at config time.
37935      * @param {Boolean} value True to allow the user to directly edit the field text
37936      */
37937     setEditable : function(value){
37938         if(value == this.editable){
37939             return;
37940         }
37941         this.editable = value;
37942         if(!value){
37943             this.el.dom.setAttribute('readOnly', true);
37944             this.el.on('mousedown', this.onTriggerClick,  this);
37945             this.el.addClass('x-combo-noedit');
37946         }else{
37947             this.el.dom.setAttribute('readOnly', false);
37948             this.el.un('mousedown', this.onTriggerClick,  this);
37949             this.el.removeClass('x-combo-noedit');
37950         }
37951     },
37952
37953     // private
37954     onBeforeLoad : function(){
37955         if(!this.hasFocus){
37956             return;
37957         }
37958         this.innerList.update(this.loadingText ?
37959                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37960         this.restrictHeight();
37961         this.selectedIndex = -1;
37962     },
37963
37964     // private
37965     onLoad : function(){
37966         if(!this.hasFocus){
37967             return;
37968         }
37969         if(this.store.getCount() > 0){
37970             this.expand();
37971             this.restrictHeight();
37972             if(this.lastQuery == this.allQuery){
37973                 if(this.editable){
37974                     this.el.dom.select();
37975                 }
37976                 if(!this.selectByValue(this.value, true)){
37977                     this.select(0, true);
37978                 }
37979             }else{
37980                 this.selectNext();
37981                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37982                     this.taTask.delay(this.typeAheadDelay);
37983                 }
37984             }
37985         }else{
37986             this.onEmptyResults();
37987         }
37988         //this.el.focus();
37989     },
37990     // private
37991     onLoadException : function()
37992     {
37993         this.collapse();
37994         Roo.log(this.store.reader.jsonData);
37995         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37996             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37997         }
37998         
37999         
38000     },
38001     // private
38002     onTypeAhead : function(){
38003         if(this.store.getCount() > 0){
38004             var r = this.store.getAt(0);
38005             var newValue = r.data[this.displayField];
38006             var len = newValue.length;
38007             var selStart = this.getRawValue().length;
38008             if(selStart != len){
38009                 this.setRawValue(newValue);
38010                 this.selectText(selStart, newValue.length);
38011             }
38012         }
38013     },
38014
38015     // private
38016     onSelect : function(record, index){
38017         if(this.fireEvent('beforeselect', this, record, index) !== false){
38018             this.setFromData(index > -1 ? record.data : false);
38019             this.collapse();
38020             this.fireEvent('select', this, record, index);
38021         }
38022     },
38023
38024     /**
38025      * Returns the currently selected field value or empty string if no value is set.
38026      * @return {String} value The selected value
38027      */
38028     getValue : function(){
38029         if(this.valueField){
38030             return typeof this.value != 'undefined' ? this.value : '';
38031         }else{
38032             return Roo.form.ComboBox.superclass.getValue.call(this);
38033         }
38034     },
38035
38036     /**
38037      * Clears any text/value currently set in the field
38038      */
38039     clearValue : function(){
38040         if(this.hiddenField){
38041             this.hiddenField.value = '';
38042         }
38043         this.value = '';
38044         this.setRawValue('');
38045         this.lastSelectionText = '';
38046         this.applyEmptyText();
38047     },
38048
38049     /**
38050      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38051      * will be displayed in the field.  If the value does not match the data value of an existing item,
38052      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38053      * Otherwise the field will be blank (although the value will still be set).
38054      * @param {String} value The value to match
38055      */
38056     setValue : function(v){
38057         var text = v;
38058         if(this.valueField){
38059             var r = this.findRecord(this.valueField, v);
38060             if(r){
38061                 text = r.data[this.displayField];
38062             }else if(this.valueNotFoundText !== undefined){
38063                 text = this.valueNotFoundText;
38064             }
38065         }
38066         this.lastSelectionText = text;
38067         if(this.hiddenField){
38068             this.hiddenField.value = v;
38069         }
38070         Roo.form.ComboBox.superclass.setValue.call(this, text);
38071         this.value = v;
38072     },
38073     /**
38074      * @property {Object} the last set data for the element
38075      */
38076     
38077     lastData : false,
38078     /**
38079      * Sets the value of the field based on a object which is related to the record format for the store.
38080      * @param {Object} value the value to set as. or false on reset?
38081      */
38082     setFromData : function(o){
38083         var dv = ''; // display value
38084         var vv = ''; // value value..
38085         this.lastData = o;
38086         if (this.displayField) {
38087             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
38088         } else {
38089             // this is an error condition!!!
38090             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
38091         }
38092         
38093         if(this.valueField){
38094             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
38095         }
38096         if(this.hiddenField){
38097             this.hiddenField.value = vv;
38098             
38099             this.lastSelectionText = dv;
38100             Roo.form.ComboBox.superclass.setValue.call(this, dv);
38101             this.value = vv;
38102             return;
38103         }
38104         // no hidden field.. - we store the value in 'value', but still display
38105         // display field!!!!
38106         this.lastSelectionText = dv;
38107         Roo.form.ComboBox.superclass.setValue.call(this, dv);
38108         this.value = vv;
38109         
38110         
38111     },
38112     // private
38113     reset : function(){
38114         // overridden so that last data is reset..
38115         this.setValue(this.originalValue);
38116         this.clearInvalid();
38117         this.lastData = false;
38118     },
38119     // private
38120     findRecord : function(prop, value){
38121         var record;
38122         if(this.store.getCount() > 0){
38123             this.store.each(function(r){
38124                 if(r.data[prop] == value){
38125                     record = r;
38126                     return false;
38127                 }
38128                 return true;
38129             });
38130         }
38131         return record;
38132     },
38133     
38134     getName: function()
38135     {
38136         // returns hidden if it's set..
38137         if (!this.rendered) {return ''};
38138         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38139         
38140     },
38141     // private
38142     onViewMove : function(e, t){
38143         this.inKeyMode = false;
38144     },
38145
38146     // private
38147     onViewOver : function(e, t){
38148         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38149             return;
38150         }
38151         var item = this.view.findItemFromChild(t);
38152         if(item){
38153             var index = this.view.indexOf(item);
38154             this.select(index, false);
38155         }
38156     },
38157
38158     // private
38159     onViewClick : function(doFocus)
38160     {
38161         var index = this.view.getSelectedIndexes()[0];
38162         var r = this.store.getAt(index);
38163         if(r){
38164             this.onSelect(r, index);
38165         }
38166         if(doFocus !== false && !this.blockFocus){
38167             this.el.focus();
38168         }
38169     },
38170
38171     // private
38172     restrictHeight : function(){
38173         this.innerList.dom.style.height = '';
38174         var inner = this.innerList.dom;
38175         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38176         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38177         this.list.beginUpdate();
38178         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38179         this.list.alignTo(this.el, this.listAlign);
38180         this.list.endUpdate();
38181     },
38182
38183     // private
38184     onEmptyResults : function(){
38185         this.collapse();
38186     },
38187
38188     /**
38189      * Returns true if the dropdown list is expanded, else false.
38190      */
38191     isExpanded : function(){
38192         return this.list.isVisible();
38193     },
38194
38195     /**
38196      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38197      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38198      * @param {String} value The data value of the item to select
38199      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38200      * selected item if it is not currently in view (defaults to true)
38201      * @return {Boolean} True if the value matched an item in the list, else false
38202      */
38203     selectByValue : function(v, scrollIntoView){
38204         if(v !== undefined && v !== null){
38205             var r = this.findRecord(this.valueField || this.displayField, v);
38206             if(r){
38207                 this.select(this.store.indexOf(r), scrollIntoView);
38208                 return true;
38209             }
38210         }
38211         return false;
38212     },
38213
38214     /**
38215      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38216      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38217      * @param {Number} index The zero-based index of the list item to select
38218      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38219      * selected item if it is not currently in view (defaults to true)
38220      */
38221     select : function(index, scrollIntoView){
38222         this.selectedIndex = index;
38223         this.view.select(index);
38224         if(scrollIntoView !== false){
38225             var el = this.view.getNode(index);
38226             if(el){
38227                 this.innerList.scrollChildIntoView(el, false);
38228             }
38229         }
38230     },
38231
38232     // private
38233     selectNext : function(){
38234         var ct = this.store.getCount();
38235         if(ct > 0){
38236             if(this.selectedIndex == -1){
38237                 this.select(0);
38238             }else if(this.selectedIndex < ct-1){
38239                 this.select(this.selectedIndex+1);
38240             }
38241         }
38242     },
38243
38244     // private
38245     selectPrev : function(){
38246         var ct = this.store.getCount();
38247         if(ct > 0){
38248             if(this.selectedIndex == -1){
38249                 this.select(0);
38250             }else if(this.selectedIndex != 0){
38251                 this.select(this.selectedIndex-1);
38252             }
38253         }
38254     },
38255
38256     // private
38257     onKeyUp : function(e){
38258         if(this.editable !== false && !e.isSpecialKey()){
38259             this.lastKey = e.getKey();
38260             this.dqTask.delay(this.queryDelay);
38261         }
38262     },
38263
38264     // private
38265     validateBlur : function(){
38266         return !this.list || !this.list.isVisible();   
38267     },
38268
38269     // private
38270     initQuery : function(){
38271         this.doQuery(this.getRawValue());
38272     },
38273
38274     // private
38275     doForce : function(){
38276         if(this.el.dom.value.length > 0){
38277             this.el.dom.value =
38278                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38279             this.applyEmptyText();
38280         }
38281     },
38282
38283     /**
38284      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38285      * query allowing the query action to be canceled if needed.
38286      * @param {String} query The SQL query to execute
38287      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38288      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38289      * saved in the current store (defaults to false)
38290      */
38291     doQuery : function(q, forceAll){
38292         if(q === undefined || q === null){
38293             q = '';
38294         }
38295         var qe = {
38296             query: q,
38297             forceAll: forceAll,
38298             combo: this,
38299             cancel:false
38300         };
38301         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38302             return false;
38303         }
38304         q = qe.query;
38305         forceAll = qe.forceAll;
38306         if(forceAll === true || (q.length >= this.minChars)){
38307             if(this.lastQuery != q || this.alwaysQuery){
38308                 this.lastQuery = q;
38309                 if(this.mode == 'local'){
38310                     this.selectedIndex = -1;
38311                     if(forceAll){
38312                         this.store.clearFilter();
38313                     }else{
38314                         this.store.filter(this.displayField, q);
38315                     }
38316                     this.onLoad();
38317                 }else{
38318                     this.store.baseParams[this.queryParam] = q;
38319                     this.store.load({
38320                         params: this.getParams(q)
38321                     });
38322                     this.expand();
38323                 }
38324             }else{
38325                 this.selectedIndex = -1;
38326                 this.onLoad();   
38327             }
38328         }
38329     },
38330
38331     // private
38332     getParams : function(q){
38333         var p = {};
38334         //p[this.queryParam] = q;
38335         if(this.pageSize){
38336             p.start = 0;
38337             p.limit = this.pageSize;
38338         }
38339         return p;
38340     },
38341
38342     /**
38343      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38344      */
38345     collapse : function(){
38346         if(!this.isExpanded()){
38347             return;
38348         }
38349         this.list.hide();
38350         Roo.get(document).un('mousedown', this.collapseIf, this);
38351         Roo.get(document).un('mousewheel', this.collapseIf, this);
38352         if (!this.editable) {
38353             Roo.get(document).un('keydown', this.listKeyPress, this);
38354         }
38355         this.fireEvent('collapse', this);
38356     },
38357
38358     // private
38359     collapseIf : function(e){
38360         if(!e.within(this.wrap) && !e.within(this.list)){
38361             this.collapse();
38362         }
38363     },
38364
38365     /**
38366      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38367      */
38368     expand : function(){
38369         if(this.isExpanded() || !this.hasFocus){
38370             return;
38371         }
38372         this.list.alignTo(this.el, this.listAlign);
38373         this.list.show();
38374         Roo.get(document).on('mousedown', this.collapseIf, this);
38375         Roo.get(document).on('mousewheel', this.collapseIf, this);
38376         if (!this.editable) {
38377             Roo.get(document).on('keydown', this.listKeyPress, this);
38378         }
38379         
38380         this.fireEvent('expand', this);
38381     },
38382
38383     // private
38384     // Implements the default empty TriggerField.onTriggerClick function
38385     onTriggerClick : function(){
38386         if(this.disabled){
38387             return;
38388         }
38389         if(this.isExpanded()){
38390             this.collapse();
38391             if (!this.blockFocus) {
38392                 this.el.focus();
38393             }
38394             
38395         }else {
38396             this.hasFocus = true;
38397             if(this.triggerAction == 'all') {
38398                 this.doQuery(this.allQuery, true);
38399             } else {
38400                 this.doQuery(this.getRawValue());
38401             }
38402             if (!this.blockFocus) {
38403                 this.el.focus();
38404             }
38405         }
38406     },
38407     listKeyPress : function(e)
38408     {
38409         //Roo.log('listkeypress');
38410         // scroll to first matching element based on key pres..
38411         if (e.isSpecialKey()) {
38412             return false;
38413         }
38414         var k = String.fromCharCode(e.getKey()).toUpperCase();
38415         //Roo.log(k);
38416         var match  = false;
38417         var csel = this.view.getSelectedNodes();
38418         var cselitem = false;
38419         if (csel.length) {
38420             var ix = this.view.indexOf(csel[0]);
38421             cselitem  = this.store.getAt(ix);
38422             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38423                 cselitem = false;
38424             }
38425             
38426         }
38427         
38428         this.store.each(function(v) { 
38429             if (cselitem) {
38430                 // start at existing selection.
38431                 if (cselitem.id == v.id) {
38432                     cselitem = false;
38433                 }
38434                 return;
38435             }
38436                 
38437             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38438                 match = this.store.indexOf(v);
38439                 return false;
38440             }
38441         }, this);
38442         
38443         if (match === false) {
38444             return true; // no more action?
38445         }
38446         // scroll to?
38447         this.view.select(match);
38448         var sn = Roo.get(this.view.getSelectedNodes()[0])
38449         sn.scrollIntoView(sn.dom.parentNode, false);
38450     }
38451
38452     /** 
38453     * @cfg {Boolean} grow 
38454     * @hide 
38455     */
38456     /** 
38457     * @cfg {Number} growMin 
38458     * @hide 
38459     */
38460     /** 
38461     * @cfg {Number} growMax 
38462     * @hide 
38463     */
38464     /**
38465      * @hide
38466      * @method autoSize
38467      */
38468 });/*
38469  * Copyright(c) 2010-2012, Roo J Solutions Limited
38470  *
38471  * Licence LGPL
38472  *
38473  */
38474
38475 /**
38476  * @class Roo.form.ComboBoxArray
38477  * @extends Roo.form.TextField
38478  * A facebook style adder... for lists of email / people / countries  etc...
38479  * pick multiple items from a combo box, and shows each one.
38480  *
38481  *  Fred [x]  Brian [x]  [Pick another |v]
38482  *
38483  *
38484  *  For this to work: it needs various extra information
38485  *    - normal combo problay has
38486  *      name, hiddenName
38487  *    + displayField, valueField
38488  *
38489  *    For our purpose...
38490  *
38491  *
38492  *   If we change from 'extends' to wrapping...
38493  *   
38494  *  
38495  *
38496  
38497  
38498  * @constructor
38499  * Create a new ComboBoxArray.
38500  * @param {Object} config Configuration options
38501  */
38502  
38503
38504 Roo.form.ComboBoxArray = function(config)
38505 {
38506     
38507     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
38508     
38509     this.items = new Roo.util.MixedCollection(false);
38510     
38511     // construct the child combo...
38512     
38513     
38514     
38515     
38516    
38517     
38518 }
38519
38520  
38521 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
38522
38523     /**
38524      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
38525      */
38526     
38527     lastData : false,
38528     
38529     // behavies liek a hiddne field
38530     inputType:      'hidden',
38531     /**
38532      * @cfg {Number} width The width of the box that displays the selected element
38533      */ 
38534     width:          300,
38535
38536     
38537     
38538     /**
38539      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
38540      */
38541     name : false,
38542     /**
38543      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
38544      */
38545     hiddenName : false,
38546     
38547     
38548     // private the array of items that are displayed..
38549     items  : false,
38550     // private - the hidden field el.
38551     hiddenEl : false,
38552     // private - the filed el..
38553     el : false,
38554     
38555     //validateValue : function() { return true; }, // all values are ok!
38556     //onAddClick: function() { },
38557     
38558     onRender : function(ct, position) 
38559     {
38560         
38561         // create the standard hidden element
38562         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
38563         
38564         
38565         // give fake names to child combo;
38566         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
38567         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
38568         
38569         this.combo = Roo.factory(this.combo, Roo.form);
38570         this.combo.onRender(ct, position);
38571         
38572         // assigned so form know we need to do this..
38573         this.store          = this.combo.store;
38574         this.valueField     = this.combo.valueField;
38575         this.displayField   = this.combo.displayField ;
38576         
38577         
38578         this.combo.wrap.addClass('x-cbarray-grp');
38579         
38580         var cbwrap = this.combo.wrap.createChild(
38581             {tag: 'div', cls: 'x-cbarray-cb'},
38582             this.combo.el.dom
38583         );
38584         
38585              
38586         this.hiddenEl = this.combo.wrap.createChild({
38587             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
38588         });
38589         this.el = this.combo.wrap.createChild({
38590             tag: 'input',  type:'hidden' , name: this.name, value : ''
38591         });
38592          //   this.el.dom.removeAttribute("name");
38593         
38594         
38595         this.outerWrap = this.combo.wrap;
38596         this.wrap = cbwrap;
38597         
38598         this.outerWrap.setWidth(this.width);
38599         this.outerWrap.dom.removeChild(this.el.dom);
38600         
38601         this.wrap.dom.appendChild(this.el.dom);
38602         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
38603         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
38604         
38605         this.combo.trigger.setStyle('position','relative');
38606         this.combo.trigger.setStyle('left', '0px');
38607         this.combo.trigger.setStyle('top', '2px');
38608         
38609         this.combo.el.setStyle('vertical-align', 'text-bottom');
38610         
38611         //this.trigger.setStyle('vertical-align', 'top');
38612         
38613         // this should use the code from combo really... on('add' ....)
38614         if (this.adder) {
38615             
38616         
38617             this.adder = this.outerWrap.createChild(
38618                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
38619             var _t = this;
38620             this.adder.on('click', function(e) {
38621                 _t.fireEvent('adderclick', this, e);
38622             }, _t);
38623         }
38624         //var _t = this;
38625         //this.adder.on('click', this.onAddClick, _t);
38626         
38627         
38628         this.combo.on('select', function(cb, rec, ix) {
38629             this.addItem(rec.data);
38630             
38631             cb.setValue('');
38632             cb.el.dom.value = '';
38633             //cb.lastData = rec.data;
38634             // add to list
38635             
38636         }, this);
38637         
38638         
38639     },
38640     
38641     
38642     getName: function()
38643     {
38644         // returns hidden if it's set..
38645         if (!this.rendered) {return ''};
38646         return  this.hiddenName ? this.hiddenName : this.name;
38647         
38648     },
38649     
38650     
38651     onResize: function(w, h){
38652         
38653         return;
38654         // not sure if this is needed..
38655         //this.combo.onResize(w,h);
38656         
38657         if(typeof w != 'number'){
38658             // we do not handle it!?!?
38659             return;
38660         }
38661         var tw = this.combo.trigger.getWidth();
38662         tw += this.addicon ? this.addicon.getWidth() : 0;
38663         tw += this.editicon ? this.editicon.getWidth() : 0;
38664         var x = w - tw;
38665         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
38666             
38667         this.combo.trigger.setStyle('left', '0px');
38668         
38669         if(this.list && this.listWidth === undefined){
38670             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
38671             this.list.setWidth(lw);
38672             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38673         }
38674         
38675     
38676         
38677     },
38678     
38679     addItem: function(rec)
38680     {
38681         var valueField = this.combo.valueField;
38682         var displayField = this.combo.displayField;
38683         if (this.items.indexOfKey(rec[valueField]) > -1) {
38684             //console.log("GOT " + rec.data.id);
38685             return;
38686         }
38687         
38688         var x = new Roo.form.ComboBoxArray.Item({
38689             //id : rec[this.idField],
38690             data : rec,
38691             displayField : displayField ,
38692             tipField : displayField ,
38693             cb : this
38694         });
38695         // use the 
38696         this.items.add(rec[valueField],x);
38697         // add it before the element..
38698         this.updateHiddenEl();
38699         x.render(this.outerWrap, this.wrap.dom);
38700         // add the image handler..
38701     },
38702     
38703     updateHiddenEl : function()
38704     {
38705         this.validate();
38706         if (!this.hiddenEl) {
38707             return;
38708         }
38709         var ar = [];
38710         var idField = this.combo.valueField;
38711         
38712         this.items.each(function(f) {
38713             ar.push(f.data[idField]);
38714            
38715         });
38716         this.hiddenEl.dom.value = ar.join(',');
38717         this.validate();
38718     },
38719     
38720     reset : function()
38721     {
38722         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
38723         this.items.each(function(f) {
38724            f.remove(); 
38725         });
38726         this.el.dom.value = '';
38727         if (this.hiddenEl) {
38728             this.hiddenEl.dom.value = '';
38729         }
38730         
38731     },
38732     getValue: function()
38733     {
38734         return this.hiddenEl ? this.hiddenEl.dom.value : '';
38735     },
38736     setValue: function(v) // not a valid action - must use addItems..
38737     {
38738          
38739         this.reset();
38740
38741         if (this.store.isLocal) {
38742             // then we can use the store to find the values..
38743             // comma seperated at present.. this needs to allow JSON based encoding..
38744             this.hiddenEl.value  = v;
38745             var v_ar = [];
38746             Roo.each(v.split(','), function(k) {
38747                 Roo.log("CHECK " + this.valueField + ',' + k);
38748                 var li = this.store.query(this.valueField, k);
38749                 if (!li.length) {
38750                     return;
38751                 }
38752                 add = {};
38753                 add[this.valueField] = k;
38754                 add[this.displayField] = li.item(0).data[this.displayField];
38755                 
38756                 this.addItem(add);
38757             }, this) 
38758             
38759                 
38760             
38761         }
38762         
38763         
38764     },
38765     setFromData: function(v)
38766     {
38767         // this recieves an object, if setValues is called.
38768         this.reset();
38769         this.el.dom.value = v[this.displayField];
38770         this.hiddenEl.dom.value = v[this.valueField];
38771         if (!v[this.valueField].length) {
38772             return;
38773         }
38774         var keys = v[this.valueField].split(',');
38775         var display = v[this.displayField].split(',');
38776         for (var i = 0 ; i < keys.length; i++) {
38777             
38778             add = {};
38779             add[this.valueField] = keys[i];
38780             add[this.displayField] = display[i];
38781             this.addItem(add);
38782         }
38783       
38784         
38785     },
38786     
38787     
38788     validateValue : function(value){
38789         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
38790         
38791     }
38792     
38793 });
38794
38795
38796
38797 /**
38798  * @class Roo.form.ComboBoxArray.Item
38799  * @extends Roo.BoxComponent
38800  * A selected item in the list
38801  *  Fred [x]  Brian [x]  [Pick another |v]
38802  * 
38803  * @constructor
38804  * Create a new item.
38805  * @param {Object} config Configuration options
38806  */
38807  
38808 Roo.form.ComboBoxArray.Item = function(config) {
38809     config.id = Roo.id();
38810     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
38811 }
38812
38813 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
38814     data : {},
38815     cb: false,
38816     displayField : false,
38817     tipField : false,
38818     
38819     
38820     defaultAutoCreate : {
38821         tag: 'div',
38822         cls: 'x-cbarray-item',
38823         cn : [ 
38824             { tag: 'div' },
38825             {
38826                 tag: 'img',
38827                 width:16,
38828                 height : 16,
38829                 src : Roo.BLANK_IMAGE_URL ,
38830                 align: 'center'
38831             }
38832         ]
38833         
38834     },
38835     
38836  
38837     onRender : function(ct, position)
38838     {
38839         Roo.form.Field.superclass.onRender.call(this, ct, position);
38840         
38841         if(!this.el){
38842             var cfg = this.getAutoCreate();
38843             this.el = ct.createChild(cfg, position);
38844         }
38845         
38846         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
38847         
38848         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
38849             this.cb.renderer(this.data) :
38850             String.format('{0}',this.data[this.displayField]);
38851         
38852             
38853         this.el.child('div').dom.setAttribute('qtip',
38854                         String.format('{0}',this.data[this.tipField])
38855         );
38856         
38857         this.el.child('img').on('click', this.remove, this);
38858         
38859     },
38860    
38861     remove : function()
38862     {
38863         
38864         this.cb.items.remove(this);
38865         this.el.child('img').un('click', this.remove, this);
38866         this.el.remove();
38867         this.cb.updateHiddenEl();
38868     }
38869     
38870     
38871 });/*
38872  * Based on:
38873  * Ext JS Library 1.1.1
38874  * Copyright(c) 2006-2007, Ext JS, LLC.
38875  *
38876  * Originally Released Under LGPL - original licence link has changed is not relivant.
38877  *
38878  * Fork - LGPL
38879  * <script type="text/javascript">
38880  */
38881 /**
38882  * @class Roo.form.Checkbox
38883  * @extends Roo.form.Field
38884  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38885  * @constructor
38886  * Creates a new Checkbox
38887  * @param {Object} config Configuration options
38888  */
38889 Roo.form.Checkbox = function(config){
38890     Roo.form.Checkbox.superclass.constructor.call(this, config);
38891     this.addEvents({
38892         /**
38893          * @event check
38894          * Fires when the checkbox is checked or unchecked.
38895              * @param {Roo.form.Checkbox} this This checkbox
38896              * @param {Boolean} checked The new checked value
38897              */
38898         check : true
38899     });
38900 };
38901
38902 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38903     /**
38904      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38905      */
38906     focusClass : undefined,
38907     /**
38908      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38909      */
38910     fieldClass: "x-form-field",
38911     /**
38912      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38913      */
38914     checked: false,
38915     /**
38916      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38917      * {tag: "input", type: "checkbox", autocomplete: "off"})
38918      */
38919     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38920     /**
38921      * @cfg {String} boxLabel The text that appears beside the checkbox
38922      */
38923     boxLabel : "",
38924     /**
38925      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38926      */  
38927     inputValue : '1',
38928     /**
38929      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38930      */
38931      valueOff: '0', // value when not checked..
38932
38933     actionMode : 'viewEl', 
38934     //
38935     // private
38936     itemCls : 'x-menu-check-item x-form-item',
38937     groupClass : 'x-menu-group-item',
38938     inputType : 'hidden',
38939     
38940     
38941     inSetChecked: false, // check that we are not calling self...
38942     
38943     inputElement: false, // real input element?
38944     basedOn: false, // ????
38945     
38946     isFormField: true, // not sure where this is needed!!!!
38947
38948     onResize : function(){
38949         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38950         if(!this.boxLabel){
38951             this.el.alignTo(this.wrap, 'c-c');
38952         }
38953     },
38954
38955     initEvents : function(){
38956         Roo.form.Checkbox.superclass.initEvents.call(this);
38957         this.el.on("click", this.onClick,  this);
38958         this.el.on("change", this.onClick,  this);
38959     },
38960
38961
38962     getResizeEl : function(){
38963         return this.wrap;
38964     },
38965
38966     getPositionEl : function(){
38967         return this.wrap;
38968     },
38969
38970     // private
38971     onRender : function(ct, position){
38972         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38973         /*
38974         if(this.inputValue !== undefined){
38975             this.el.dom.value = this.inputValue;
38976         }
38977         */
38978         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38979         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38980         var viewEl = this.wrap.createChild({ 
38981             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38982         this.viewEl = viewEl;   
38983         this.wrap.on('click', this.onClick,  this); 
38984         
38985         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38986         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38987         
38988         
38989         
38990         if(this.boxLabel){
38991             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38992         //    viewEl.on('click', this.onClick,  this); 
38993         }
38994         //if(this.checked){
38995             this.setChecked(this.checked);
38996         //}else{
38997             //this.checked = this.el.dom;
38998         //}
38999
39000     },
39001
39002     // private
39003     initValue : Roo.emptyFn,
39004
39005     /**
39006      * Returns the checked state of the checkbox.
39007      * @return {Boolean} True if checked, else false
39008      */
39009     getValue : function(){
39010         if(this.el){
39011             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
39012         }
39013         return this.valueOff;
39014         
39015     },
39016
39017         // private
39018     onClick : function(){ 
39019         this.setChecked(!this.checked);
39020
39021         //if(this.el.dom.checked != this.checked){
39022         //    this.setValue(this.el.dom.checked);
39023        // }
39024     },
39025
39026     /**
39027      * Sets the checked state of the checkbox.
39028      * On is always based on a string comparison between inputValue and the param.
39029      * @param {Boolean/String} value - the value to set 
39030      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
39031      */
39032     setValue : function(v,suppressEvent){
39033         
39034         
39035         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
39036         //if(this.el && this.el.dom){
39037         //    this.el.dom.checked = this.checked;
39038         //    this.el.dom.defaultChecked = this.checked;
39039         //}
39040         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
39041         //this.fireEvent("check", this, this.checked);
39042     },
39043     // private..
39044     setChecked : function(state,suppressEvent)
39045     {
39046         if (this.inSetChecked) {
39047             this.checked = state;
39048             return;
39049         }
39050         
39051     
39052         if(this.wrap){
39053             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
39054         }
39055         this.checked = state;
39056         if(suppressEvent !== true){
39057             this.fireEvent('check', this, state);
39058         }
39059         this.inSetChecked = true;
39060         this.el.dom.value = state ? this.inputValue : this.valueOff;
39061         this.inSetChecked = false;
39062         
39063     },
39064     // handle setting of hidden value by some other method!!?!?
39065     setFromHidden: function()
39066     {
39067         if(!this.el){
39068             return;
39069         }
39070         //console.log("SET FROM HIDDEN");
39071         //alert('setFrom hidden');
39072         this.setValue(this.el.dom.value);
39073     },
39074     
39075     onDestroy : function()
39076     {
39077         if(this.viewEl){
39078             Roo.get(this.viewEl).remove();
39079         }
39080          
39081         Roo.form.Checkbox.superclass.onDestroy.call(this);
39082     }
39083
39084 });/*
39085  * Based on:
39086  * Ext JS Library 1.1.1
39087  * Copyright(c) 2006-2007, Ext JS, LLC.
39088  *
39089  * Originally Released Under LGPL - original licence link has changed is not relivant.
39090  *
39091  * Fork - LGPL
39092  * <script type="text/javascript">
39093  */
39094  
39095 /**
39096  * @class Roo.form.Radio
39097  * @extends Roo.form.Checkbox
39098  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
39099  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
39100  * @constructor
39101  * Creates a new Radio
39102  * @param {Object} config Configuration options
39103  */
39104 Roo.form.Radio = function(){
39105     Roo.form.Radio.superclass.constructor.apply(this, arguments);
39106 };
39107 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
39108     inputType: 'radio',
39109
39110     /**
39111      * If this radio is part of a group, it will return the selected value
39112      * @return {String}
39113      */
39114     getGroupValue : function(){
39115         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
39116     }
39117 });//<script type="text/javascript">
39118
39119 /*
39120  * Ext JS Library 1.1.1
39121  * Copyright(c) 2006-2007, Ext JS, LLC.
39122  * licensing@extjs.com
39123  * 
39124  * http://www.extjs.com/license
39125  */
39126  
39127  /*
39128   * 
39129   * Known bugs:
39130   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
39131   * - IE ? - no idea how much works there.
39132   * 
39133   * 
39134   * 
39135   */
39136  
39137
39138 /**
39139  * @class Ext.form.HtmlEditor
39140  * @extends Ext.form.Field
39141  * Provides a lightweight HTML Editor component.
39142  *
39143  * This has been tested on Fireforx / Chrome.. IE may not be so great..
39144  * 
39145  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
39146  * supported by this editor.</b><br/><br/>
39147  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
39148  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
39149  */
39150 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
39151       /**
39152      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
39153      */
39154     toolbars : false,
39155     /**
39156      * @cfg {String} createLinkText The default text for the create link prompt
39157      */
39158     createLinkText : 'Please enter the URL for the link:',
39159     /**
39160      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
39161      */
39162     defaultLinkValue : 'http:/'+'/',
39163    
39164      /**
39165      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
39166      *                        Roo.resizable.
39167      */
39168     resizable : false,
39169      /**
39170      * @cfg {Number} height (in pixels)
39171      */   
39172     height: 300,
39173    /**
39174      * @cfg {Number} width (in pixels)
39175      */   
39176     width: 500,
39177     
39178     /**
39179      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
39180      * 
39181      */
39182     stylesheets: false,
39183     
39184     // id of frame..
39185     frameId: false,
39186     
39187     // private properties
39188     validationEvent : false,
39189     deferHeight: true,
39190     initialized : false,
39191     activated : false,
39192     sourceEditMode : false,
39193     onFocus : Roo.emptyFn,
39194     iframePad:3,
39195     hideMode:'offsets',
39196     
39197     defaultAutoCreate : { // modified by initCompnoent..
39198         tag: "textarea",
39199         style:"width:500px;height:300px;",
39200         autocomplete: "off"
39201     },
39202
39203     // private
39204     initComponent : function(){
39205         this.addEvents({
39206             /**
39207              * @event initialize
39208              * Fires when the editor is fully initialized (including the iframe)
39209              * @param {HtmlEditor} this
39210              */
39211             initialize: true,
39212             /**
39213              * @event activate
39214              * Fires when the editor is first receives the focus. Any insertion must wait
39215              * until after this event.
39216              * @param {HtmlEditor} this
39217              */
39218             activate: true,
39219              /**
39220              * @event beforesync
39221              * Fires before the textarea is updated with content from the editor iframe. Return false
39222              * to cancel the sync.
39223              * @param {HtmlEditor} this
39224              * @param {String} html
39225              */
39226             beforesync: true,
39227              /**
39228              * @event beforepush
39229              * Fires before the iframe editor is updated with content from the textarea. Return false
39230              * to cancel the push.
39231              * @param {HtmlEditor} this
39232              * @param {String} html
39233              */
39234             beforepush: true,
39235              /**
39236              * @event sync
39237              * Fires when the textarea is updated with content from the editor iframe.
39238              * @param {HtmlEditor} this
39239              * @param {String} html
39240              */
39241             sync: true,
39242              /**
39243              * @event push
39244              * Fires when the iframe editor is updated with content from the textarea.
39245              * @param {HtmlEditor} this
39246              * @param {String} html
39247              */
39248             push: true,
39249              /**
39250              * @event editmodechange
39251              * Fires when the editor switches edit modes
39252              * @param {HtmlEditor} this
39253              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
39254              */
39255             editmodechange: true,
39256             /**
39257              * @event editorevent
39258              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
39259              * @param {HtmlEditor} this
39260              */
39261             editorevent: true
39262         });
39263         this.defaultAutoCreate =  {
39264             tag: "textarea",
39265             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
39266             autocomplete: "off"
39267         };
39268     },
39269
39270     /**
39271      * Protected method that will not generally be called directly. It
39272      * is called when the editor creates its toolbar. Override this method if you need to
39273      * add custom toolbar buttons.
39274      * @param {HtmlEditor} editor
39275      */
39276     createToolbar : function(editor){
39277         if (!editor.toolbars || !editor.toolbars.length) {
39278             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
39279         }
39280         
39281         for (var i =0 ; i < editor.toolbars.length;i++) {
39282             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
39283             editor.toolbars[i].init(editor);
39284         }
39285          
39286         
39287     },
39288
39289     /**
39290      * Protected method that will not generally be called directly. It
39291      * is called when the editor initializes the iframe with HTML contents. Override this method if you
39292      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
39293      */
39294     getDocMarkup : function(){
39295         // body styles..
39296         var st = '';
39297         if (this.stylesheets === false) {
39298             
39299             Roo.get(document.head).select('style').each(function(node) {
39300                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
39301             });
39302             
39303             Roo.get(document.head).select('link').each(function(node) { 
39304                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
39305             });
39306             
39307         } else if (!this.stylesheets.length) {
39308                 // simple..
39309                 st = '<style type="text/css">' +
39310                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
39311                    '</style>';
39312         } else {
39313             Roo.each(this.stylesheets, function(s) {
39314                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
39315             });
39316             
39317         }
39318         
39319         st +=  '<style type="text/css">' +
39320             'IMG { cursor: pointer } ' +
39321         '</style>';
39322
39323         
39324         return '<html><head>' + st  +
39325             //<style type="text/css">' +
39326             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
39327             //'</style>' +
39328             ' </head><body class="roo-htmleditor-body"></body></html>';
39329     },
39330
39331     // private
39332     onRender : function(ct, position)
39333     {
39334         var _t = this;
39335         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
39336         this.el.dom.style.border = '0 none';
39337         this.el.dom.setAttribute('tabIndex', -1);
39338         this.el.addClass('x-hidden');
39339         if(Roo.isIE){ // fix IE 1px bogus margin
39340             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
39341         }
39342         this.wrap = this.el.wrap({
39343             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
39344         });
39345         
39346         if (this.resizable) {
39347             this.resizeEl = new Roo.Resizable(this.wrap, {
39348                 pinned : true,
39349                 wrap: true,
39350                 dynamic : true,
39351                 minHeight : this.height,
39352                 height: this.height,
39353                 handles : this.resizable,
39354                 width: this.width,
39355                 listeners : {
39356                     resize : function(r, w, h) {
39357                         _t.onResize(w,h); // -something
39358                     }
39359                 }
39360             });
39361             
39362         }
39363
39364         this.frameId = Roo.id();
39365         
39366         this.createToolbar(this);
39367         
39368       
39369         
39370         var iframe = this.wrap.createChild({
39371             tag: 'iframe',
39372             id: this.frameId,
39373             name: this.frameId,
39374             frameBorder : 'no',
39375             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
39376         }, this.el
39377         );
39378         
39379        // console.log(iframe);
39380         //this.wrap.dom.appendChild(iframe);
39381
39382         this.iframe = iframe.dom;
39383
39384          this.assignDocWin();
39385         
39386         this.doc.designMode = 'on';
39387        
39388         this.doc.open();
39389         this.doc.write(this.getDocMarkup());
39390         this.doc.close();
39391
39392         
39393         var task = { // must defer to wait for browser to be ready
39394             run : function(){
39395                 //console.log("run task?" + this.doc.readyState);
39396                 this.assignDocWin();
39397                 if(this.doc.body || this.doc.readyState == 'complete'){
39398                     try {
39399                         this.doc.designMode="on";
39400                     } catch (e) {
39401                         return;
39402                     }
39403                     Roo.TaskMgr.stop(task);
39404                     this.initEditor.defer(10, this);
39405                 }
39406             },
39407             interval : 10,
39408             duration:10000,
39409             scope: this
39410         };
39411         Roo.TaskMgr.start(task);
39412
39413         if(!this.width){
39414             this.setSize(this.wrap.getSize());
39415         }
39416         if (this.resizeEl) {
39417             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
39418             // should trigger onReize..
39419         }
39420     },
39421
39422     // private
39423     onResize : function(w, h)
39424     {
39425         //Roo.log('resize: ' +w + ',' + h );
39426         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
39427         if(this.el && this.iframe){
39428             if(typeof w == 'number'){
39429                 var aw = w - this.wrap.getFrameWidth('lr');
39430                 this.el.setWidth(this.adjustWidth('textarea', aw));
39431                 this.iframe.style.width = aw + 'px';
39432             }
39433             if(typeof h == 'number'){
39434                 var tbh = 0;
39435                 for (var i =0; i < this.toolbars.length;i++) {
39436                     // fixme - ask toolbars for heights?
39437                     tbh += this.toolbars[i].tb.el.getHeight();
39438                     if (this.toolbars[i].footer) {
39439                         tbh += this.toolbars[i].footer.el.getHeight();
39440                     }
39441                 }
39442                 
39443                 
39444                 
39445                 
39446                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
39447                 ah -= 5; // knock a few pixes off for look..
39448                 this.el.setHeight(this.adjustWidth('textarea', ah));
39449                 this.iframe.style.height = ah + 'px';
39450                 if(this.doc){
39451                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
39452                 }
39453             }
39454         }
39455     },
39456
39457     /**
39458      * Toggles the editor between standard and source edit mode.
39459      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
39460      */
39461     toggleSourceEdit : function(sourceEditMode){
39462         
39463         this.sourceEditMode = sourceEditMode === true;
39464         
39465         if(this.sourceEditMode){
39466           
39467             this.syncValue();
39468             this.iframe.className = 'x-hidden';
39469             this.el.removeClass('x-hidden');
39470             this.el.dom.removeAttribute('tabIndex');
39471             this.el.focus();
39472         }else{
39473              
39474             this.pushValue();
39475             this.iframe.className = '';
39476             this.el.addClass('x-hidden');
39477             this.el.dom.setAttribute('tabIndex', -1);
39478             this.deferFocus();
39479         }
39480         this.setSize(this.wrap.getSize());
39481         this.fireEvent('editmodechange', this, this.sourceEditMode);
39482     },
39483
39484     // private used internally
39485     createLink : function(){
39486         var url = prompt(this.createLinkText, this.defaultLinkValue);
39487         if(url && url != 'http:/'+'/'){
39488             this.relayCmd('createlink', url);
39489         }
39490     },
39491
39492     // private (for BoxComponent)
39493     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39494
39495     // private (for BoxComponent)
39496     getResizeEl : function(){
39497         return this.wrap;
39498     },
39499
39500     // private (for BoxComponent)
39501     getPositionEl : function(){
39502         return this.wrap;
39503     },
39504
39505     // private
39506     initEvents : function(){
39507         this.originalValue = this.getValue();
39508     },
39509
39510     /**
39511      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39512      * @method
39513      */
39514     markInvalid : Roo.emptyFn,
39515     /**
39516      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39517      * @method
39518      */
39519     clearInvalid : Roo.emptyFn,
39520
39521     setValue : function(v){
39522         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
39523         this.pushValue();
39524     },
39525
39526     /**
39527      * Protected method that will not generally be called directly. If you need/want
39528      * custom HTML cleanup, this is the method you should override.
39529      * @param {String} html The HTML to be cleaned
39530      * return {String} The cleaned HTML
39531      */
39532     cleanHtml : function(html){
39533         html = String(html);
39534         if(html.length > 5){
39535             if(Roo.isSafari){ // strip safari nonsense
39536                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39537             }
39538         }
39539         if(html == '&nbsp;'){
39540             html = '';
39541         }
39542         return html;
39543     },
39544
39545     /**
39546      * Protected method that will not generally be called directly. Syncs the contents
39547      * of the editor iframe with the textarea.
39548      */
39549     syncValue : function(){
39550         if(this.initialized){
39551             var bd = (this.doc.body || this.doc.documentElement);
39552             //this.cleanUpPaste(); -- this is done else where and causes havoc..
39553             var html = bd.innerHTML;
39554             if(Roo.isSafari){
39555                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39556                 var m = bs.match(/text-align:(.*?);/i);
39557                 if(m && m[1]){
39558                     html = '<div style="'+m[0]+'">' + html + '</div>';
39559                 }
39560             }
39561             html = this.cleanHtml(html);
39562             // fix up the special chars..
39563             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
39564                 return "&#"+b.charCodeAt()+";" 
39565             });
39566             if(this.fireEvent('beforesync', this, html) !== false){
39567                 this.el.dom.value = html;
39568                 this.fireEvent('sync', this, html);
39569             }
39570         }
39571     },
39572
39573     /**
39574      * Protected method that will not generally be called directly. Pushes the value of the textarea
39575      * into the iframe editor.
39576      */
39577     pushValue : function(){
39578         if(this.initialized){
39579             var v = this.el.dom.value;
39580             if(v.length < 1){
39581                 v = '&#160;';
39582             }
39583             
39584             if(this.fireEvent('beforepush', this, v) !== false){
39585                 var d = (this.doc.body || this.doc.documentElement);
39586                 d.innerHTML = v;
39587                 this.cleanUpPaste();
39588                 this.el.dom.value = d.innerHTML;
39589                 this.fireEvent('push', this, v);
39590             }
39591         }
39592     },
39593
39594     // private
39595     deferFocus : function(){
39596         this.focus.defer(10, this);
39597     },
39598
39599     // doc'ed in Field
39600     focus : function(){
39601         if(this.win && !this.sourceEditMode){
39602             this.win.focus();
39603         }else{
39604             this.el.focus();
39605         }
39606     },
39607     
39608     assignDocWin: function()
39609     {
39610         var iframe = this.iframe;
39611         
39612          if(Roo.isIE){
39613             this.doc = iframe.contentWindow.document;
39614             this.win = iframe.contentWindow;
39615         } else {
39616             if (!Roo.get(this.frameId)) {
39617                 return;
39618             }
39619             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39620             this.win = Roo.get(this.frameId).dom.contentWindow;
39621         }
39622     },
39623     
39624     // private
39625     initEditor : function(){
39626         //console.log("INIT EDITOR");
39627         this.assignDocWin();
39628         
39629         
39630         
39631         this.doc.designMode="on";
39632         this.doc.open();
39633         this.doc.write(this.getDocMarkup());
39634         this.doc.close();
39635         
39636         var dbody = (this.doc.body || this.doc.documentElement);
39637         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39638         // this copies styles from the containing element into thsi one..
39639         // not sure why we need all of this..
39640         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39641         ss['background-attachment'] = 'fixed'; // w3c
39642         dbody.bgProperties = 'fixed'; // ie
39643         Roo.DomHelper.applyStyles(dbody, ss);
39644         Roo.EventManager.on(this.doc, {
39645             //'mousedown': this.onEditorEvent,
39646             'mouseup': this.onEditorEvent,
39647             'dblclick': this.onEditorEvent,
39648             'click': this.onEditorEvent,
39649             'keyup': this.onEditorEvent,
39650             buffer:100,
39651             scope: this
39652         });
39653         if(Roo.isGecko){
39654             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39655         }
39656         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39657             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39658         }
39659         this.initialized = true;
39660
39661         this.fireEvent('initialize', this);
39662         this.pushValue();
39663     },
39664
39665     // private
39666     onDestroy : function(){
39667         
39668         
39669         
39670         if(this.rendered){
39671             
39672             for (var i =0; i < this.toolbars.length;i++) {
39673                 // fixme - ask toolbars for heights?
39674                 this.toolbars[i].onDestroy();
39675             }
39676             
39677             this.wrap.dom.innerHTML = '';
39678             this.wrap.remove();
39679         }
39680     },
39681
39682     // private
39683     onFirstFocus : function(){
39684         
39685         this.assignDocWin();
39686         
39687         
39688         this.activated = true;
39689         for (var i =0; i < this.toolbars.length;i++) {
39690             this.toolbars[i].onFirstFocus();
39691         }
39692        
39693         if(Roo.isGecko){ // prevent silly gecko errors
39694             this.win.focus();
39695             var s = this.win.getSelection();
39696             if(!s.focusNode || s.focusNode.nodeType != 3){
39697                 var r = s.getRangeAt(0);
39698                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39699                 r.collapse(true);
39700                 this.deferFocus();
39701             }
39702             try{
39703                 this.execCmd('useCSS', true);
39704                 this.execCmd('styleWithCSS', false);
39705             }catch(e){}
39706         }
39707         this.fireEvent('activate', this);
39708     },
39709
39710     // private
39711     adjustFont: function(btn){
39712         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39713         //if(Roo.isSafari){ // safari
39714         //    adjust *= 2;
39715        // }
39716         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39717         if(Roo.isSafari){ // safari
39718             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39719             v =  (v < 10) ? 10 : v;
39720             v =  (v > 48) ? 48 : v;
39721             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39722             
39723         }
39724         
39725         
39726         v = Math.max(1, v+adjust);
39727         
39728         this.execCmd('FontSize', v  );
39729     },
39730
39731     onEditorEvent : function(e){
39732         this.fireEvent('editorevent', this, e);
39733       //  this.updateToolbar();
39734         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39735     },
39736
39737     insertTag : function(tg)
39738     {
39739         // could be a bit smarter... -> wrap the current selected tRoo..
39740         
39741         this.execCmd("formatblock",   tg);
39742         
39743     },
39744     
39745     insertText : function(txt)
39746     {
39747         
39748         
39749         range = this.createRange();
39750         range.deleteContents();
39751                //alert(Sender.getAttribute('label'));
39752                
39753         range.insertNode(this.doc.createTextNode(txt));
39754     } ,
39755     
39756     // private
39757     relayBtnCmd : function(btn){
39758         this.relayCmd(btn.cmd);
39759     },
39760
39761     /**
39762      * Executes a Midas editor command on the editor document and performs necessary focus and
39763      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39764      * @param {String} cmd The Midas command
39765      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39766      */
39767     relayCmd : function(cmd, value){
39768         this.win.focus();
39769         this.execCmd(cmd, value);
39770         this.fireEvent('editorevent', this);
39771         //this.updateToolbar();
39772         this.deferFocus();
39773     },
39774
39775     /**
39776      * Executes a Midas editor command directly on the editor document.
39777      * For visual commands, you should use {@link #relayCmd} instead.
39778      * <b>This should only be called after the editor is initialized.</b>
39779      * @param {String} cmd The Midas command
39780      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39781      */
39782     execCmd : function(cmd, value){
39783         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39784         this.syncValue();
39785     },
39786  
39787  
39788    
39789     /**
39790      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39791      * to insert tRoo.
39792      * @param {String} text | dom node.. 
39793      */
39794     insertAtCursor : function(text)
39795     {
39796         
39797         
39798         
39799         if(!this.activated){
39800             return;
39801         }
39802         /*
39803         if(Roo.isIE){
39804             this.win.focus();
39805             var r = this.doc.selection.createRange();
39806             if(r){
39807                 r.collapse(true);
39808                 r.pasteHTML(text);
39809                 this.syncValue();
39810                 this.deferFocus();
39811             
39812             }
39813             return;
39814         }
39815         */
39816         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39817             this.win.focus();
39818             
39819             
39820             // from jquery ui (MIT licenced)
39821             var range, node;
39822             var win = this.win;
39823             
39824             if (win.getSelection && win.getSelection().getRangeAt) {
39825                 range = win.getSelection().getRangeAt(0);
39826                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39827                 range.insertNode(node);
39828             } else if (win.document.selection && win.document.selection.createRange) {
39829                 // no firefox support
39830                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39831                 win.document.selection.createRange().pasteHTML(txt);
39832             } else {
39833                 // no firefox support
39834                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39835                 this.execCmd('InsertHTML', txt);
39836             } 
39837             
39838             this.syncValue();
39839             
39840             this.deferFocus();
39841         }
39842     },
39843  // private
39844     mozKeyPress : function(e){
39845         if(e.ctrlKey){
39846             var c = e.getCharCode(), cmd;
39847           
39848             if(c > 0){
39849                 c = String.fromCharCode(c).toLowerCase();
39850                 switch(c){
39851                     case 'b':
39852                         cmd = 'bold';
39853                         break;
39854                     case 'i':
39855                         cmd = 'italic';
39856                         break;
39857                     
39858                     case 'u':
39859                         cmd = 'underline';
39860                         break;
39861                     
39862                     case 'v':
39863                         this.cleanUpPaste.defer(100, this);
39864                         return;
39865                         
39866                 }
39867                 if(cmd){
39868                     this.win.focus();
39869                     this.execCmd(cmd);
39870                     this.deferFocus();
39871                     e.preventDefault();
39872                 }
39873                 
39874             }
39875         }
39876     },
39877
39878     // private
39879     fixKeys : function(){ // load time branching for fastest keydown performance
39880         if(Roo.isIE){
39881             return function(e){
39882                 var k = e.getKey(), r;
39883                 if(k == e.TAB){
39884                     e.stopEvent();
39885                     r = this.doc.selection.createRange();
39886                     if(r){
39887                         r.collapse(true);
39888                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39889                         this.deferFocus();
39890                     }
39891                     return;
39892                 }
39893                 
39894                 if(k == e.ENTER){
39895                     r = this.doc.selection.createRange();
39896                     if(r){
39897                         var target = r.parentElement();
39898                         if(!target || target.tagName.toLowerCase() != 'li'){
39899                             e.stopEvent();
39900                             r.pasteHTML('<br />');
39901                             r.collapse(false);
39902                             r.select();
39903                         }
39904                     }
39905                 }
39906                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39907                     this.cleanUpPaste.defer(100, this);
39908                     return;
39909                 }
39910                 
39911                 
39912             };
39913         }else if(Roo.isOpera){
39914             return function(e){
39915                 var k = e.getKey();
39916                 if(k == e.TAB){
39917                     e.stopEvent();
39918                     this.win.focus();
39919                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39920                     this.deferFocus();
39921                 }
39922                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39923                     this.cleanUpPaste.defer(100, this);
39924                     return;
39925                 }
39926                 
39927             };
39928         }else if(Roo.isSafari){
39929             return function(e){
39930                 var k = e.getKey();
39931                 
39932                 if(k == e.TAB){
39933                     e.stopEvent();
39934                     this.execCmd('InsertText','\t');
39935                     this.deferFocus();
39936                     return;
39937                 }
39938                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39939                     this.cleanUpPaste.defer(100, this);
39940                     return;
39941                 }
39942                 
39943              };
39944         }
39945     }(),
39946     
39947     getAllAncestors: function()
39948     {
39949         var p = this.getSelectedNode();
39950         var a = [];
39951         if (!p) {
39952             a.push(p); // push blank onto stack..
39953             p = this.getParentElement();
39954         }
39955         
39956         
39957         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39958             a.push(p);
39959             p = p.parentNode;
39960         }
39961         a.push(this.doc.body);
39962         return a;
39963     },
39964     lastSel : false,
39965     lastSelNode : false,
39966     
39967     
39968     getSelection : function() 
39969     {
39970         this.assignDocWin();
39971         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39972     },
39973     
39974     getSelectedNode: function() 
39975     {
39976         // this may only work on Gecko!!!
39977         
39978         // should we cache this!!!!
39979         
39980         
39981         
39982          
39983         var range = this.createRange(this.getSelection()).cloneRange();
39984         
39985         if (Roo.isIE) {
39986             var parent = range.parentElement();
39987             while (true) {
39988                 var testRange = range.duplicate();
39989                 testRange.moveToElementText(parent);
39990                 if (testRange.inRange(range)) {
39991                     break;
39992                 }
39993                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39994                     break;
39995                 }
39996                 parent = parent.parentElement;
39997             }
39998             return parent;
39999         }
40000         
40001         // is ancestor a text element.
40002         var ac =  range.commonAncestorContainer;
40003         if (ac.nodeType == 3) {
40004             ac = ac.parentNode;
40005         }
40006         
40007         var ar = ac.childNodes;
40008          
40009         var nodes = [];
40010         var other_nodes = [];
40011         var has_other_nodes = false;
40012         for (var i=0;i<ar.length;i++) {
40013             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
40014                 continue;
40015             }
40016             // fullly contained node.
40017             
40018             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
40019                 nodes.push(ar[i]);
40020                 continue;
40021             }
40022             
40023             // probably selected..
40024             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
40025                 other_nodes.push(ar[i]);
40026                 continue;
40027             }
40028             // outer..
40029             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
40030                 continue;
40031             }
40032             
40033             
40034             has_other_nodes = true;
40035         }
40036         if (!nodes.length && other_nodes.length) {
40037             nodes= other_nodes;
40038         }
40039         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
40040             return false;
40041         }
40042         
40043         return nodes[0];
40044     },
40045     createRange: function(sel)
40046     {
40047         // this has strange effects when using with 
40048         // top toolbar - not sure if it's a great idea.
40049         //this.editor.contentWindow.focus();
40050         if (typeof sel != "undefined") {
40051             try {
40052                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
40053             } catch(e) {
40054                 return this.doc.createRange();
40055             }
40056         } else {
40057             return this.doc.createRange();
40058         }
40059     },
40060     getParentElement: function()
40061     {
40062         
40063         this.assignDocWin();
40064         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
40065         
40066         var range = this.createRange(sel);
40067          
40068         try {
40069             var p = range.commonAncestorContainer;
40070             while (p.nodeType == 3) { // text node
40071                 p = p.parentNode;
40072             }
40073             return p;
40074         } catch (e) {
40075             return null;
40076         }
40077     
40078     },
40079     /***
40080      *
40081      * Range intersection.. the hard stuff...
40082      *  '-1' = before
40083      *  '0' = hits..
40084      *  '1' = after.
40085      *         [ -- selected range --- ]
40086      *   [fail]                        [fail]
40087      *
40088      *    basically..
40089      *      if end is before start or  hits it. fail.
40090      *      if start is after end or hits it fail.
40091      *
40092      *   if either hits (but other is outside. - then it's not 
40093      *   
40094      *    
40095      **/
40096     
40097     
40098     // @see http://www.thismuchiknow.co.uk/?p=64.
40099     rangeIntersectsNode : function(range, node)
40100     {
40101         var nodeRange = node.ownerDocument.createRange();
40102         try {
40103             nodeRange.selectNode(node);
40104         } catch (e) {
40105             nodeRange.selectNodeContents(node);
40106         }
40107     
40108         var rangeStartRange = range.cloneRange();
40109         rangeStartRange.collapse(true);
40110     
40111         var rangeEndRange = range.cloneRange();
40112         rangeEndRange.collapse(false);
40113     
40114         var nodeStartRange = nodeRange.cloneRange();
40115         nodeStartRange.collapse(true);
40116     
40117         var nodeEndRange = nodeRange.cloneRange();
40118         nodeEndRange.collapse(false);
40119     
40120         return rangeStartRange.compareBoundaryPoints(
40121                  Range.START_TO_START, nodeEndRange) == -1 &&
40122                rangeEndRange.compareBoundaryPoints(
40123                  Range.START_TO_START, nodeStartRange) == 1;
40124         
40125          
40126     },
40127     rangeCompareNode : function(range, node)
40128     {
40129         var nodeRange = node.ownerDocument.createRange();
40130         try {
40131             nodeRange.selectNode(node);
40132         } catch (e) {
40133             nodeRange.selectNodeContents(node);
40134         }
40135         
40136         
40137         range.collapse(true);
40138     
40139         nodeRange.collapse(true);
40140      
40141         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
40142         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
40143          
40144         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
40145         
40146         var nodeIsBefore   =  ss == 1;
40147         var nodeIsAfter    = ee == -1;
40148         
40149         if (nodeIsBefore && nodeIsAfter)
40150             return 0; // outer
40151         if (!nodeIsBefore && nodeIsAfter)
40152             return 1; //right trailed.
40153         
40154         if (nodeIsBefore && !nodeIsAfter)
40155             return 2;  // left trailed.
40156         // fully contined.
40157         return 3;
40158     },
40159
40160     // private? - in a new class?
40161     cleanUpPaste :  function()
40162     {
40163         // cleans up the whole document..
40164          Roo.log('cleanuppaste');
40165         this.cleanUpChildren(this.doc.body);
40166         var clean = this.cleanWordChars(this.doc.body.innerHTML);
40167         if (clean != this.doc.body.innerHTML) {
40168             this.doc.body.innerHTML = clean;
40169         }
40170         
40171     },
40172     
40173     cleanWordChars : function(input) {
40174         var he = Roo.form.HtmlEditor;
40175     
40176         var output = input;
40177         Roo.each(he.swapCodes, function(sw) { 
40178         
40179             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
40180             output = output.replace(swapper, sw[1]);
40181         });
40182         return output;
40183     },
40184     
40185     
40186     cleanUpChildren : function (n)
40187     {
40188         if (!n.childNodes.length) {
40189             return;
40190         }
40191         for (var i = n.childNodes.length-1; i > -1 ; i--) {
40192            this.cleanUpChild(n.childNodes[i]);
40193         }
40194     },
40195     
40196     
40197         
40198     
40199     cleanUpChild : function (node)
40200     {
40201         //console.log(node);
40202         if (node.nodeName == "#text") {
40203             // clean up silly Windows -- stuff?
40204             return; 
40205         }
40206         if (node.nodeName == "#comment") {
40207             node.parentNode.removeChild(node);
40208             // clean up silly Windows -- stuff?
40209             return; 
40210         }
40211         
40212         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
40213             // remove node.
40214             node.parentNode.removeChild(node);
40215             return;
40216             
40217         }
40218         
40219         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
40220         
40221         // remove <a name=....> as rendering on yahoo mailer is bored with this.
40222         
40223         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
40224             remove_keep_children = true;
40225         }
40226         
40227         if (remove_keep_children) {
40228             this.cleanUpChildren(node);
40229             // inserts everything just before this node...
40230             while (node.childNodes.length) {
40231                 var cn = node.childNodes[0];
40232                 node.removeChild(cn);
40233                 node.parentNode.insertBefore(cn, node);
40234             }
40235             node.parentNode.removeChild(node);
40236             return;
40237         }
40238         
40239         if (!node.attributes || !node.attributes.length) {
40240             this.cleanUpChildren(node);
40241             return;
40242         }
40243         
40244         function cleanAttr(n,v)
40245         {
40246             
40247             if (v.match(/^\./) || v.match(/^\//)) {
40248                 return;
40249             }
40250             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
40251                 return;
40252             }
40253             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
40254             node.removeAttribute(n);
40255             
40256         }
40257         
40258         function cleanStyle(n,v)
40259         {
40260             if (v.match(/expression/)) { //XSS?? should we even bother..
40261                 node.removeAttribute(n);
40262                 return;
40263             }
40264             
40265             
40266             var parts = v.split(/;/);
40267             Roo.each(parts, function(p) {
40268                 p = p.replace(/\s+/g,'');
40269                 if (!p.length) {
40270                     return true;
40271                 }
40272                 var l = p.split(':').shift().replace(/\s+/g,'');
40273                 
40274                 // only allow 'c whitelisted system attributes'
40275                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
40276                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
40277                     node.removeAttribute(n);
40278                     return false;
40279                 }
40280                 return true;
40281             });
40282             
40283             
40284         }
40285         
40286         
40287         for (var i = node.attributes.length-1; i > -1 ; i--) {
40288             var a = node.attributes[i];
40289             //console.log(a);
40290             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
40291                 node.removeAttribute(a.name);
40292                 return;
40293             }
40294             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
40295                 cleanAttr(a.name,a.value); // fixme..
40296                 return;
40297             }
40298             if (a.name == 'style') {
40299                 cleanStyle(a.name,a.value);
40300             }
40301             /// clean up MS crap..
40302             // tecnically this should be a list of valid class'es..
40303             
40304             
40305             if (a.name == 'class') {
40306                 if (a.value.match(/^Mso/)) {
40307                     node.className = '';
40308                 }
40309                 
40310                 if (a.value.match(/body/)) {
40311                     node.className = '';
40312                 }
40313             }
40314             
40315             // style cleanup!?
40316             // class cleanup?
40317             
40318         }
40319         
40320         
40321         this.cleanUpChildren(node);
40322         
40323         
40324     }
40325     
40326     
40327     // hide stuff that is not compatible
40328     /**
40329      * @event blur
40330      * @hide
40331      */
40332     /**
40333      * @event change
40334      * @hide
40335      */
40336     /**
40337      * @event focus
40338      * @hide
40339      */
40340     /**
40341      * @event specialkey
40342      * @hide
40343      */
40344     /**
40345      * @cfg {String} fieldClass @hide
40346      */
40347     /**
40348      * @cfg {String} focusClass @hide
40349      */
40350     /**
40351      * @cfg {String} autoCreate @hide
40352      */
40353     /**
40354      * @cfg {String} inputType @hide
40355      */
40356     /**
40357      * @cfg {String} invalidClass @hide
40358      */
40359     /**
40360      * @cfg {String} invalidText @hide
40361      */
40362     /**
40363      * @cfg {String} msgFx @hide
40364      */
40365     /**
40366      * @cfg {String} validateOnBlur @hide
40367      */
40368 });
40369
40370 Roo.form.HtmlEditor.white = [
40371         'area', 'br', 'img', 'input', 'hr', 'wbr',
40372         
40373        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
40374        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
40375        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
40376        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
40377        'table',   'ul',         'xmp', 
40378        
40379        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
40380       'thead',   'tr', 
40381      
40382       'dir', 'menu', 'ol', 'ul', 'dl',
40383        
40384       'embed',  'object'
40385 ];
40386
40387
40388 Roo.form.HtmlEditor.black = [
40389     //    'embed',  'object', // enable - backend responsiblity to clean thiese
40390         'applet', // 
40391         'base',   'basefont', 'bgsound', 'blink',  'body', 
40392         'frame',  'frameset', 'head',    'html',   'ilayer', 
40393         'iframe', 'layer',  'link',     'meta',    'object',   
40394         'script', 'style' ,'title',  'xml' // clean later..
40395 ];
40396 Roo.form.HtmlEditor.clean = [
40397     'script', 'style', 'title', 'xml'
40398 ];
40399 Roo.form.HtmlEditor.remove = [
40400     'font'
40401 ];
40402 // attributes..
40403
40404 Roo.form.HtmlEditor.ablack = [
40405     'on'
40406 ];
40407     
40408 Roo.form.HtmlEditor.aclean = [ 
40409     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
40410 ];
40411
40412 // protocols..
40413 Roo.form.HtmlEditor.pwhite= [
40414         'http',  'https',  'mailto'
40415 ];
40416
40417 // white listed style attributes.
40418 Roo.form.HtmlEditor.cwhite= [
40419         'text-align',
40420         'font-size'
40421 ];
40422
40423
40424 Roo.form.HtmlEditor.swapCodes   =[ 
40425     [    8211, "--" ], 
40426     [    8212, "--" ], 
40427     [    8216,  "'" ],  
40428     [    8217, "'" ],  
40429     [    8220, '"' ],  
40430     [    8221, '"' ],  
40431     [    8226, "*" ],  
40432     [    8230, "..." ]
40433 ]; 
40434
40435     // <script type="text/javascript">
40436 /*
40437  * Based on
40438  * Ext JS Library 1.1.1
40439  * Copyright(c) 2006-2007, Ext JS, LLC.
40440  *  
40441  
40442  */
40443
40444 /**
40445  * @class Roo.form.HtmlEditorToolbar1
40446  * Basic Toolbar
40447  * 
40448  * Usage:
40449  *
40450  new Roo.form.HtmlEditor({
40451     ....
40452     toolbars : [
40453         new Roo.form.HtmlEditorToolbar1({
40454             disable : { fonts: 1 , format: 1, ..., ... , ...],
40455             btns : [ .... ]
40456         })
40457     }
40458      
40459  * 
40460  * @cfg {Object} disable List of elements to disable..
40461  * @cfg {Array} btns List of additional buttons.
40462  * 
40463  * 
40464  * NEEDS Extra CSS? 
40465  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
40466  */
40467  
40468 Roo.form.HtmlEditor.ToolbarStandard = function(config)
40469 {
40470     
40471     Roo.apply(this, config);
40472     
40473     // default disabled, based on 'good practice'..
40474     this.disable = this.disable || {};
40475     Roo.applyIf(this.disable, {
40476         fontSize : true,
40477         colors : true,
40478         specialElements : true
40479     });
40480     
40481     
40482     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40483     // dont call parent... till later.
40484 }
40485
40486 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
40487     
40488     tb: false,
40489     
40490     rendered: false,
40491     
40492     editor : false,
40493     /**
40494      * @cfg {Object} disable  List of toolbar elements to disable
40495          
40496      */
40497     disable : false,
40498       /**
40499      * @cfg {Array} fontFamilies An array of available font families
40500      */
40501     fontFamilies : [
40502         'Arial',
40503         'Courier New',
40504         'Tahoma',
40505         'Times New Roman',
40506         'Verdana'
40507     ],
40508     
40509     specialChars : [
40510            "&#169;",
40511           "&#174;",     
40512           "&#8482;",    
40513           "&#163;" ,    
40514          // "&#8212;",    
40515           "&#8230;",    
40516           "&#247;" ,    
40517         //  "&#225;" ,     ?? a acute?
40518            "&#8364;"    , //Euro
40519        //   "&#8220;"    ,
40520         //  "&#8221;"    ,
40521         //  "&#8226;"    ,
40522           "&#176;"  //   , // degrees
40523
40524          // "&#233;"     , // e ecute
40525          // "&#250;"     , // u ecute?
40526     ],
40527     
40528     specialElements : [
40529         {
40530             text: "Insert Table",
40531             xtype: 'MenuItem',
40532             xns : Roo.Menu,
40533             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
40534                 
40535         },
40536         {    
40537             text: "Insert Image",
40538             xtype: 'MenuItem',
40539             xns : Roo.Menu,
40540             ihtml : '<img src="about:blank"/>'
40541             
40542         }
40543         
40544          
40545     ],
40546     
40547     
40548     inputElements : [ 
40549             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40550             "input:submit", "input:button", "select", "textarea", "label" ],
40551     formats : [
40552         ["p"] ,  
40553         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40554         ["pre"],[ "code"], 
40555         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40556     ],
40557      /**
40558      * @cfg {String} defaultFont default font to use.
40559      */
40560     defaultFont: 'tahoma',
40561    
40562     fontSelect : false,
40563     
40564     
40565     formatCombo : false,
40566     
40567     init : function(editor)
40568     {
40569         this.editor = editor;
40570         
40571         
40572         var fid = editor.frameId;
40573         var etb = this;
40574         function btn(id, toggle, handler){
40575             var xid = fid + '-'+ id ;
40576             return {
40577                 id : xid,
40578                 cmd : id,
40579                 cls : 'x-btn-icon x-edit-'+id,
40580                 enableToggle:toggle !== false,
40581                 scope: editor, // was editor...
40582                 handler:handler||editor.relayBtnCmd,
40583                 clickEvent:'mousedown',
40584                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40585                 tabIndex:-1
40586             };
40587         }
40588         
40589         
40590         
40591         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40592         this.tb = tb;
40593          // stop form submits
40594         tb.el.on('click', function(e){
40595             e.preventDefault(); // what does this do?
40596         });
40597
40598         if(!this.disable.font && !Roo.isSafari){
40599             /* why no safari for fonts
40600             editor.fontSelect = tb.el.createChild({
40601                 tag:'select',
40602                 tabIndex: -1,
40603                 cls:'x-font-select',
40604                 html: editor.createFontOptions()
40605             });
40606             editor.fontSelect.on('change', function(){
40607                 var font = editor.fontSelect.dom.value;
40608                 editor.relayCmd('fontname', font);
40609                 editor.deferFocus();
40610             }, editor);
40611             tb.add(
40612                 editor.fontSelect.dom,
40613                 '-'
40614             );
40615             */
40616         };
40617         if(!this.disable.formats){
40618             this.formatCombo = new Roo.form.ComboBox({
40619                 store: new Roo.data.SimpleStore({
40620                     id : 'tag',
40621                     fields: ['tag'],
40622                     data : this.formats // from states.js
40623                 }),
40624                 blockFocus : true,
40625                 //autoCreate : {tag: "div",  size: "20"},
40626                 displayField:'tag',
40627                 typeAhead: false,
40628                 mode: 'local',
40629                 editable : false,
40630                 triggerAction: 'all',
40631                 emptyText:'Add tag',
40632                 selectOnFocus:true,
40633                 width:135,
40634                 listeners : {
40635                     'select': function(c, r, i) {
40636                         editor.insertTag(r.get('tag'));
40637                         editor.focus();
40638                     }
40639                 }
40640
40641             });
40642             tb.addField(this.formatCombo);
40643             
40644         }
40645         
40646         if(!this.disable.format){
40647             tb.add(
40648                 btn('bold'),
40649                 btn('italic'),
40650                 btn('underline')
40651             );
40652         };
40653         if(!this.disable.fontSize){
40654             tb.add(
40655                 '-',
40656                 
40657                 
40658                 btn('increasefontsize', false, editor.adjustFont),
40659                 btn('decreasefontsize', false, editor.adjustFont)
40660             );
40661         };
40662         
40663         
40664         if(!this.disable.colors){
40665             tb.add(
40666                 '-', {
40667                     id:editor.frameId +'-forecolor',
40668                     cls:'x-btn-icon x-edit-forecolor',
40669                     clickEvent:'mousedown',
40670                     tooltip: this.buttonTips['forecolor'] || undefined,
40671                     tabIndex:-1,
40672                     menu : new Roo.menu.ColorMenu({
40673                         allowReselect: true,
40674                         focus: Roo.emptyFn,
40675                         value:'000000',
40676                         plain:true,
40677                         selectHandler: function(cp, color){
40678                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40679                             editor.deferFocus();
40680                         },
40681                         scope: editor,
40682                         clickEvent:'mousedown'
40683                     })
40684                 }, {
40685                     id:editor.frameId +'backcolor',
40686                     cls:'x-btn-icon x-edit-backcolor',
40687                     clickEvent:'mousedown',
40688                     tooltip: this.buttonTips['backcolor'] || undefined,
40689                     tabIndex:-1,
40690                     menu : new Roo.menu.ColorMenu({
40691                         focus: Roo.emptyFn,
40692                         value:'FFFFFF',
40693                         plain:true,
40694                         allowReselect: true,
40695                         selectHandler: function(cp, color){
40696                             if(Roo.isGecko){
40697                                 editor.execCmd('useCSS', false);
40698                                 editor.execCmd('hilitecolor', color);
40699                                 editor.execCmd('useCSS', true);
40700                                 editor.deferFocus();
40701                             }else{
40702                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40703                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40704                                 editor.deferFocus();
40705                             }
40706                         },
40707                         scope:editor,
40708                         clickEvent:'mousedown'
40709                     })
40710                 }
40711             );
40712         };
40713         // now add all the items...
40714         
40715
40716         if(!this.disable.alignments){
40717             tb.add(
40718                 '-',
40719                 btn('justifyleft'),
40720                 btn('justifycenter'),
40721                 btn('justifyright')
40722             );
40723         };
40724
40725         //if(!Roo.isSafari){
40726             if(!this.disable.links){
40727                 tb.add(
40728                     '-',
40729                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40730                 );
40731             };
40732
40733             if(!this.disable.lists){
40734                 tb.add(
40735                     '-',
40736                     btn('insertorderedlist'),
40737                     btn('insertunorderedlist')
40738                 );
40739             }
40740             if(!this.disable.sourceEdit){
40741                 tb.add(
40742                     '-',
40743                     btn('sourceedit', true, function(btn){
40744                         this.toggleSourceEdit(btn.pressed);
40745                     })
40746                 );
40747             }
40748         //}
40749         
40750         var smenu = { };
40751         // special menu.. - needs to be tidied up..
40752         if (!this.disable.special) {
40753             smenu = {
40754                 text: "&#169;",
40755                 cls: 'x-edit-none',
40756                 
40757                 menu : {
40758                     items : []
40759                 }
40760             };
40761             for (var i =0; i < this.specialChars.length; i++) {
40762                 smenu.menu.items.push({
40763                     
40764                     html: this.specialChars[i],
40765                     handler: function(a,b) {
40766                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40767                         //editor.insertAtCursor(a.html);
40768                         
40769                     },
40770                     tabIndex:-1
40771                 });
40772             }
40773             
40774             
40775             tb.add(smenu);
40776             
40777             
40778         }
40779          
40780         if (!this.disable.specialElements) {
40781             var semenu = {
40782                 text: "Other;",
40783                 cls: 'x-edit-none',
40784                 menu : {
40785                     items : []
40786                 }
40787             };
40788             for (var i =0; i < this.specialElements.length; i++) {
40789                 semenu.menu.items.push(
40790                     Roo.apply({ 
40791                         handler: function(a,b) {
40792                             editor.insertAtCursor(this.ihtml);
40793                         }
40794                     }, this.specialElements[i])
40795                 );
40796                     
40797             }
40798             
40799             tb.add(semenu);
40800             
40801             
40802         }
40803          
40804         
40805         if (this.btns) {
40806             for(var i =0; i< this.btns.length;i++) {
40807                 var b = Roo.factory(this.btns[i],Roo.form);
40808                 b.cls =  'x-edit-none';
40809                 b.scope = editor;
40810                 tb.add(b);
40811             }
40812         
40813         }
40814         
40815         
40816         
40817         // disable everything...
40818         
40819         this.tb.items.each(function(item){
40820            if(item.id != editor.frameId+ '-sourceedit'){
40821                 item.disable();
40822             }
40823         });
40824         this.rendered = true;
40825         
40826         // the all the btns;
40827         editor.on('editorevent', this.updateToolbar, this);
40828         // other toolbars need to implement this..
40829         //editor.on('editmodechange', this.updateToolbar, this);
40830     },
40831     
40832     
40833     
40834     /**
40835      * Protected method that will not generally be called directly. It triggers
40836      * a toolbar update by reading the markup state of the current selection in the editor.
40837      */
40838     updateToolbar: function(){
40839
40840         if(!this.editor.activated){
40841             this.editor.onFirstFocus();
40842             return;
40843         }
40844
40845         var btns = this.tb.items.map, 
40846             doc = this.editor.doc,
40847             frameId = this.editor.frameId;
40848
40849         if(!this.disable.font && !Roo.isSafari){
40850             /*
40851             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40852             if(name != this.fontSelect.dom.value){
40853                 this.fontSelect.dom.value = name;
40854             }
40855             */
40856         }
40857         if(!this.disable.format){
40858             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40859             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40860             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40861         }
40862         if(!this.disable.alignments){
40863             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40864             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40865             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40866         }
40867         if(!Roo.isSafari && !this.disable.lists){
40868             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40869             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40870         }
40871         
40872         var ans = this.editor.getAllAncestors();
40873         if (this.formatCombo) {
40874             
40875             
40876             var store = this.formatCombo.store;
40877             this.formatCombo.setValue("");
40878             for (var i =0; i < ans.length;i++) {
40879                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40880                     // select it..
40881                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40882                     break;
40883                 }
40884             }
40885         }
40886         
40887         
40888         
40889         // hides menus... - so this cant be on a menu...
40890         Roo.menu.MenuMgr.hideAll();
40891
40892         //this.editorsyncValue();
40893     },
40894    
40895     
40896     createFontOptions : function(){
40897         var buf = [], fs = this.fontFamilies, ff, lc;
40898         for(var i = 0, len = fs.length; i< len; i++){
40899             ff = fs[i];
40900             lc = ff.toLowerCase();
40901             buf.push(
40902                 '<option value="',lc,'" style="font-family:',ff,';"',
40903                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40904                     ff,
40905                 '</option>'
40906             );
40907         }
40908         return buf.join('');
40909     },
40910     
40911     toggleSourceEdit : function(sourceEditMode){
40912         if(sourceEditMode === undefined){
40913             sourceEditMode = !this.sourceEditMode;
40914         }
40915         this.sourceEditMode = sourceEditMode === true;
40916         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40917         // just toggle the button?
40918         if(btn.pressed !== this.editor.sourceEditMode){
40919             btn.toggle(this.editor.sourceEditMode);
40920             return;
40921         }
40922         
40923         if(this.sourceEditMode){
40924             this.tb.items.each(function(item){
40925                 if(item.cmd != 'sourceedit'){
40926                     item.disable();
40927                 }
40928             });
40929           
40930         }else{
40931             if(this.initialized){
40932                 this.tb.items.each(function(item){
40933                     item.enable();
40934                 });
40935             }
40936             
40937         }
40938         // tell the editor that it's been pressed..
40939         this.editor.toggleSourceEdit(sourceEditMode);
40940        
40941     },
40942      /**
40943      * Object collection of toolbar tooltips for the buttons in the editor. The key
40944      * is the command id associated with that button and the value is a valid QuickTips object.
40945      * For example:
40946 <pre><code>
40947 {
40948     bold : {
40949         title: 'Bold (Ctrl+B)',
40950         text: 'Make the selected text bold.',
40951         cls: 'x-html-editor-tip'
40952     },
40953     italic : {
40954         title: 'Italic (Ctrl+I)',
40955         text: 'Make the selected text italic.',
40956         cls: 'x-html-editor-tip'
40957     },
40958     ...
40959 </code></pre>
40960     * @type Object
40961      */
40962     buttonTips : {
40963         bold : {
40964             title: 'Bold (Ctrl+B)',
40965             text: 'Make the selected text bold.',
40966             cls: 'x-html-editor-tip'
40967         },
40968         italic : {
40969             title: 'Italic (Ctrl+I)',
40970             text: 'Make the selected text italic.',
40971             cls: 'x-html-editor-tip'
40972         },
40973         underline : {
40974             title: 'Underline (Ctrl+U)',
40975             text: 'Underline the selected text.',
40976             cls: 'x-html-editor-tip'
40977         },
40978         increasefontsize : {
40979             title: 'Grow Text',
40980             text: 'Increase the font size.',
40981             cls: 'x-html-editor-tip'
40982         },
40983         decreasefontsize : {
40984             title: 'Shrink Text',
40985             text: 'Decrease the font size.',
40986             cls: 'x-html-editor-tip'
40987         },
40988         backcolor : {
40989             title: 'Text Highlight Color',
40990             text: 'Change the background color of the selected text.',
40991             cls: 'x-html-editor-tip'
40992         },
40993         forecolor : {
40994             title: 'Font Color',
40995             text: 'Change the color of the selected text.',
40996             cls: 'x-html-editor-tip'
40997         },
40998         justifyleft : {
40999             title: 'Align Text Left',
41000             text: 'Align text to the left.',
41001             cls: 'x-html-editor-tip'
41002         },
41003         justifycenter : {
41004             title: 'Center Text',
41005             text: 'Center text in the editor.',
41006             cls: 'x-html-editor-tip'
41007         },
41008         justifyright : {
41009             title: 'Align Text Right',
41010             text: 'Align text to the right.',
41011             cls: 'x-html-editor-tip'
41012         },
41013         insertunorderedlist : {
41014             title: 'Bullet List',
41015             text: 'Start a bulleted list.',
41016             cls: 'x-html-editor-tip'
41017         },
41018         insertorderedlist : {
41019             title: 'Numbered List',
41020             text: 'Start a numbered list.',
41021             cls: 'x-html-editor-tip'
41022         },
41023         createlink : {
41024             title: 'Hyperlink',
41025             text: 'Make the selected text a hyperlink.',
41026             cls: 'x-html-editor-tip'
41027         },
41028         sourceedit : {
41029             title: 'Source Edit',
41030             text: 'Switch to source editing mode.',
41031             cls: 'x-html-editor-tip'
41032         }
41033     },
41034     // private
41035     onDestroy : function(){
41036         if(this.rendered){
41037             
41038             this.tb.items.each(function(item){
41039                 if(item.menu){
41040                     item.menu.removeAll();
41041                     if(item.menu.el){
41042                         item.menu.el.destroy();
41043                     }
41044                 }
41045                 item.destroy();
41046             });
41047              
41048         }
41049     },
41050     onFirstFocus: function() {
41051         this.tb.items.each(function(item){
41052            item.enable();
41053         });
41054     }
41055 });
41056
41057
41058
41059
41060 // <script type="text/javascript">
41061 /*
41062  * Based on
41063  * Ext JS Library 1.1.1
41064  * Copyright(c) 2006-2007, Ext JS, LLC.
41065  *  
41066  
41067  */
41068
41069  
41070 /**
41071  * @class Roo.form.HtmlEditor.ToolbarContext
41072  * Context Toolbar
41073  * 
41074  * Usage:
41075  *
41076  new Roo.form.HtmlEditor({
41077     ....
41078     toolbars : [
41079         { xtype: 'ToolbarStandard', styles : {} }
41080         { xtype: 'ToolbarContext', disable : {} }
41081     ]
41082 })
41083
41084      
41085  * 
41086  * @config : {Object} disable List of elements to disable.. (not done yet.)
41087  * @config : {Object} styles  Map of styles available.
41088  * 
41089  */
41090
41091 Roo.form.HtmlEditor.ToolbarContext = function(config)
41092 {
41093     
41094     Roo.apply(this, config);
41095     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41096     // dont call parent... till later.
41097     this.styles = this.styles || {};
41098 }
41099 Roo.form.HtmlEditor.ToolbarContext.types = {
41100     'IMG' : {
41101         width : {
41102             title: "Width",
41103             width: 40
41104         },
41105         height:  {
41106             title: "Height",
41107             width: 40
41108         },
41109         align: {
41110             title: "Align",
41111             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
41112             width : 80
41113             
41114         },
41115         border: {
41116             title: "Border",
41117             width: 40
41118         },
41119         alt: {
41120             title: "Alt",
41121             width: 120
41122         },
41123         src : {
41124             title: "Src",
41125             width: 220
41126         }
41127         
41128     },
41129     'A' : {
41130         name : {
41131             title: "Name",
41132             width: 50
41133         },
41134         href:  {
41135             title: "Href",
41136             width: 220
41137         } // border?
41138         
41139     },
41140     'TABLE' : {
41141         rows : {
41142             title: "Rows",
41143             width: 20
41144         },
41145         cols : {
41146             title: "Cols",
41147             width: 20
41148         },
41149         width : {
41150             title: "Width",
41151             width: 40
41152         },
41153         height : {
41154             title: "Height",
41155             width: 40
41156         },
41157         border : {
41158             title: "Border",
41159             width: 20
41160         }
41161     },
41162     'TD' : {
41163         width : {
41164             title: "Width",
41165             width: 40
41166         },
41167         height : {
41168             title: "Height",
41169             width: 40
41170         },   
41171         align: {
41172             title: "Align",
41173             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
41174             width: 80
41175         },
41176         valign: {
41177             title: "Valign",
41178             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
41179             width: 80
41180         },
41181         colspan: {
41182             title: "Colspan",
41183             width: 20
41184             
41185         }
41186     },
41187     'INPUT' : {
41188         name : {
41189             title: "name",
41190             width: 120
41191         },
41192         value : {
41193             title: "Value",
41194             width: 120
41195         },
41196         width : {
41197             title: "Width",
41198             width: 40
41199         }
41200     },
41201     'LABEL' : {
41202         'for' : {
41203             title: "For",
41204             width: 120
41205         }
41206     },
41207     'TEXTAREA' : {
41208           name : {
41209             title: "name",
41210             width: 120
41211         },
41212         rows : {
41213             title: "Rows",
41214             width: 20
41215         },
41216         cols : {
41217             title: "Cols",
41218             width: 20
41219         }
41220     },
41221     'SELECT' : {
41222         name : {
41223             title: "name",
41224             width: 120
41225         },
41226         selectoptions : {
41227             title: "Options",
41228             width: 200
41229         }
41230     },
41231     
41232     // should we really allow this??
41233     // should this just be 
41234     'BODY' : {
41235         title : {
41236             title: "title",
41237             width: 200,
41238             disabled : true
41239         }
41240     },
41241     '*' : {
41242         // empty..
41243     }
41244 };
41245
41246
41247
41248 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
41249     
41250     tb: false,
41251     
41252     rendered: false,
41253     
41254     editor : false,
41255     /**
41256      * @cfg {Object} disable  List of toolbar elements to disable
41257          
41258      */
41259     disable : false,
41260     /**
41261      * @cfg {Object} styles List of styles 
41262      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
41263      *
41264      * These must be defined in the page, so they get rendered correctly..
41265      * .headline { }
41266      * TD.underline { }
41267      * 
41268      */
41269     styles : false,
41270     
41271     
41272     
41273     toolbars : false,
41274     
41275     init : function(editor)
41276     {
41277         this.editor = editor;
41278         
41279         
41280         var fid = editor.frameId;
41281         var etb = this;
41282         function btn(id, toggle, handler){
41283             var xid = fid + '-'+ id ;
41284             return {
41285                 id : xid,
41286                 cmd : id,
41287                 cls : 'x-btn-icon x-edit-'+id,
41288                 enableToggle:toggle !== false,
41289                 scope: editor, // was editor...
41290                 handler:handler||editor.relayBtnCmd,
41291                 clickEvent:'mousedown',
41292                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41293                 tabIndex:-1
41294             };
41295         }
41296         // create a new element.
41297         var wdiv = editor.wrap.createChild({
41298                 tag: 'div'
41299             }, editor.wrap.dom.firstChild.nextSibling, true);
41300         
41301         // can we do this more than once??
41302         
41303          // stop form submits
41304       
41305  
41306         // disable everything...
41307         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
41308         this.toolbars = {};
41309            
41310         for (var i in  ty) {
41311           
41312             this.toolbars[i] = this.buildToolbar(ty[i],i);
41313         }
41314         this.tb = this.toolbars.BODY;
41315         this.tb.el.show();
41316         this.buildFooter();
41317         this.footer.show();
41318         editor.on('hide', function( ) { this.footer.hide() }, this);
41319         editor.on('show', function( ) { this.footer.show() }, this);
41320         
41321          
41322         this.rendered = true;
41323         
41324         // the all the btns;
41325         editor.on('editorevent', this.updateToolbar, this);
41326         // other toolbars need to implement this..
41327         //editor.on('editmodechange', this.updateToolbar, this);
41328     },
41329     
41330     
41331     
41332     /**
41333      * Protected method that will not generally be called directly. It triggers
41334      * a toolbar update by reading the markup state of the current selection in the editor.
41335      */
41336     updateToolbar: function(editor,ev,sel){
41337
41338         //Roo.log(ev);
41339         // capture mouse up - this is handy for selecting images..
41340         // perhaps should go somewhere else...
41341         if(!this.editor.activated){
41342              this.editor.onFirstFocus();
41343             return;
41344         }
41345         
41346         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
41347         // selectNode - might want to handle IE?
41348         if (ev &&
41349             (ev.type == 'mouseup' || ev.type == 'click' ) &&
41350             ev.target && ev.target.tagName == 'IMG') {
41351             // they have click on an image...
41352             // let's see if we can change the selection...
41353             sel = ev.target;
41354          
41355               var nodeRange = sel.ownerDocument.createRange();
41356             try {
41357                 nodeRange.selectNode(sel);
41358             } catch (e) {
41359                 nodeRange.selectNodeContents(sel);
41360             }
41361             //nodeRange.collapse(true);
41362             var s = editor.win.getSelection();
41363             s.removeAllRanges();
41364             s.addRange(nodeRange);
41365         }  
41366         
41367       
41368         var updateFooter = sel ? false : true;
41369         
41370         
41371         var ans = this.editor.getAllAncestors();
41372         
41373         // pick
41374         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
41375         
41376         if (!sel) { 
41377             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
41378             sel = sel ? sel : this.editor.doc.body;
41379             sel = sel.tagName.length ? sel : this.editor.doc.body;
41380             
41381         }
41382         // pick a menu that exists..
41383         var tn = sel.tagName.toUpperCase();
41384         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
41385         
41386         tn = sel.tagName.toUpperCase();
41387         
41388         var lastSel = this.tb.selectedNode
41389         
41390         this.tb.selectedNode = sel;
41391         
41392         // if current menu does not match..
41393         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
41394                 
41395             this.tb.el.hide();
41396             ///console.log("show: " + tn);
41397             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
41398             this.tb.el.show();
41399             // update name
41400             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
41401             
41402             
41403             // update attributes
41404             if (this.tb.fields) {
41405                 this.tb.fields.each(function(e) {
41406                    e.setValue(sel.getAttribute(e.attrname));
41407                 });
41408             }
41409             
41410             var hasStyles = false;
41411             for(var i in this.styles) {
41412                 hasStyles = true;
41413                 break;
41414             }
41415             
41416             // update styles
41417             if (hasStyles) { 
41418                 var st = this.tb.fields.item(0);
41419                 
41420                 st.store.removeAll();
41421                
41422                 
41423                 var cn = sel.className.split(/\s+/);
41424                 
41425                 var avs = [];
41426                 if (this.styles['*']) {
41427                     
41428                     Roo.each(this.styles['*'], function(v) {
41429                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41430                     });
41431                 }
41432                 if (this.styles[tn]) { 
41433                     Roo.each(this.styles[tn], function(v) {
41434                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41435                     });
41436                 }
41437                 
41438                 st.store.loadData(avs);
41439                 st.collapse();
41440                 st.setValue(cn);
41441             }
41442             // flag our selected Node.
41443             this.tb.selectedNode = sel;
41444            
41445            
41446             Roo.menu.MenuMgr.hideAll();
41447
41448         }
41449         
41450         if (!updateFooter) {
41451             return;
41452         }
41453         // update the footer
41454         //
41455         var html = '';
41456         
41457         this.footerEls = ans.reverse();
41458         Roo.each(this.footerEls, function(a,i) {
41459             if (!a) { return; }
41460             html += html.length ? ' &gt; '  :  '';
41461             
41462             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
41463             
41464         });
41465        
41466         // 
41467         var sz = this.footDisp.up('td').getSize();
41468         this.footDisp.dom.style.width = (sz.width -10) + 'px';
41469         this.footDisp.dom.style.marginLeft = '5px';
41470         
41471         this.footDisp.dom.style.overflow = 'hidden';
41472         
41473         this.footDisp.dom.innerHTML = html;
41474             
41475         //this.editorsyncValue();
41476     },
41477    
41478        
41479     // private
41480     onDestroy : function(){
41481         if(this.rendered){
41482             
41483             this.tb.items.each(function(item){
41484                 if(item.menu){
41485                     item.menu.removeAll();
41486                     if(item.menu.el){
41487                         item.menu.el.destroy();
41488                     }
41489                 }
41490                 item.destroy();
41491             });
41492              
41493         }
41494     },
41495     onFirstFocus: function() {
41496         // need to do this for all the toolbars..
41497         this.tb.items.each(function(item){
41498            item.enable();
41499         });
41500     },
41501     buildToolbar: function(tlist, nm)
41502     {
41503         var editor = this.editor;
41504          // create a new element.
41505         var wdiv = editor.wrap.createChild({
41506                 tag: 'div'
41507             }, editor.wrap.dom.firstChild.nextSibling, true);
41508         
41509        
41510         var tb = new Roo.Toolbar(wdiv);
41511         // add the name..
41512         
41513         tb.add(nm+ ":&nbsp;");
41514         
41515         var styles = [];
41516         for(var i in this.styles) {
41517             styles.push(i);
41518         }
41519         
41520         // styles...
41521         if (styles && styles.length) {
41522             
41523             // this needs a multi-select checkbox...
41524             tb.addField( new Roo.form.ComboBox({
41525                 store: new Roo.data.SimpleStore({
41526                     id : 'val',
41527                     fields: ['val', 'selected'],
41528                     data : [] 
41529                 }),
41530                 name : '-roo-edit-className',
41531                 attrname : 'className',
41532                 displayField:'val',
41533                 typeAhead: false,
41534                 mode: 'local',
41535                 editable : false,
41536                 triggerAction: 'all',
41537                 emptyText:'Select Style',
41538                 selectOnFocus:true,
41539                 width: 130,
41540                 listeners : {
41541                     'select': function(c, r, i) {
41542                         // initial support only for on class per el..
41543                         tb.selectedNode.className =  r ? r.get('val') : '';
41544                         editor.syncValue();
41545                     }
41546                 }
41547     
41548             }));
41549         }
41550             
41551         
41552         
41553         for (var i in tlist) {
41554             
41555             var item = tlist[i];
41556             tb.add(item.title + ":&nbsp;");
41557             
41558             
41559             
41560             
41561             if (item.opts) {
41562                 // opts == pulldown..
41563                 tb.addField( new Roo.form.ComboBox({
41564                     store: new Roo.data.SimpleStore({
41565                         id : 'val',
41566                         fields: ['val'],
41567                         data : item.opts  
41568                     }),
41569                     name : '-roo-edit-' + i,
41570                     attrname : i,
41571                     displayField:'val',
41572                     typeAhead: false,
41573                     mode: 'local',
41574                     editable : false,
41575                     triggerAction: 'all',
41576                     emptyText:'Select',
41577                     selectOnFocus:true,
41578                     width: item.width ? item.width  : 130,
41579                     listeners : {
41580                         'select': function(c, r, i) {
41581                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
41582                         }
41583                     }
41584
41585                 }));
41586                 continue;
41587                     
41588                  
41589                 
41590                 tb.addField( new Roo.form.TextField({
41591                     name: i,
41592                     width: 100,
41593                     //allowBlank:false,
41594                     value: ''
41595                 }));
41596                 continue;
41597             }
41598             tb.addField( new Roo.form.TextField({
41599                 name: '-roo-edit-' + i,
41600                 attrname : i,
41601                 
41602                 width: item.width,
41603                 //allowBlank:true,
41604                 value: '',
41605                 listeners: {
41606                     'change' : function(f, nv, ov) {
41607                         tb.selectedNode.setAttribute(f.attrname, nv);
41608                     }
41609                 }
41610             }));
41611              
41612         }
41613         tb.el.on('click', function(e){
41614             e.preventDefault(); // what does this do?
41615         });
41616         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41617         tb.el.hide();
41618         tb.name = nm;
41619         // dont need to disable them... as they will get hidden
41620         return tb;
41621          
41622         
41623     },
41624     buildFooter : function()
41625     {
41626         
41627         var fel = this.editor.wrap.createChild();
41628         this.footer = new Roo.Toolbar(fel);
41629         // toolbar has scrolly on left / right?
41630         var footDisp= new Roo.Toolbar.Fill();
41631         var _t = this;
41632         this.footer.add(
41633             {
41634                 text : '&lt;',
41635                 xtype: 'Button',
41636                 handler : function() {
41637                     _t.footDisp.scrollTo('left',0,true)
41638                 }
41639             }
41640         );
41641         this.footer.add( footDisp );
41642         this.footer.add( 
41643             {
41644                 text : '&gt;',
41645                 xtype: 'Button',
41646                 handler : function() {
41647                     // no animation..
41648                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41649                 }
41650             }
41651         );
41652         var fel = Roo.get(footDisp.el);
41653         fel.addClass('x-editor-context');
41654         this.footDispWrap = fel; 
41655         this.footDispWrap.overflow  = 'hidden';
41656         
41657         this.footDisp = fel.createChild();
41658         this.footDispWrap.on('click', this.onContextClick, this)
41659         
41660         
41661     },
41662     onContextClick : function (ev,dom)
41663     {
41664         ev.preventDefault();
41665         var  cn = dom.className;
41666         Roo.log(cn);
41667         if (!cn.match(/x-ed-loc-/)) {
41668             return;
41669         }
41670         var n = cn.split('-').pop();
41671         var ans = this.footerEls;
41672         var sel = ans[n];
41673         
41674          // pick
41675         var range = this.editor.createRange();
41676         
41677         range.selectNodeContents(sel);
41678         //range.selectNode(sel);
41679         
41680         
41681         var selection = this.editor.getSelection();
41682         selection.removeAllRanges();
41683         selection.addRange(range);
41684         
41685         
41686         
41687         this.updateToolbar(null, null, sel);
41688         
41689         
41690     }
41691     
41692     
41693     
41694     
41695     
41696 });
41697
41698
41699
41700
41701
41702 /*
41703  * Based on:
41704  * Ext JS Library 1.1.1
41705  * Copyright(c) 2006-2007, Ext JS, LLC.
41706  *
41707  * Originally Released Under LGPL - original licence link has changed is not relivant.
41708  *
41709  * Fork - LGPL
41710  * <script type="text/javascript">
41711  */
41712  
41713 /**
41714  * @class Roo.form.BasicForm
41715  * @extends Roo.util.Observable
41716  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41717  * @constructor
41718  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41719  * @param {Object} config Configuration options
41720  */
41721 Roo.form.BasicForm = function(el, config){
41722     this.allItems = [];
41723     this.childForms = [];
41724     Roo.apply(this, config);
41725     /*
41726      * The Roo.form.Field items in this form.
41727      * @type MixedCollection
41728      */
41729      
41730      
41731     this.items = new Roo.util.MixedCollection(false, function(o){
41732         return o.id || (o.id = Roo.id());
41733     });
41734     this.addEvents({
41735         /**
41736          * @event beforeaction
41737          * Fires before any action is performed. Return false to cancel the action.
41738          * @param {Form} this
41739          * @param {Action} action The action to be performed
41740          */
41741         beforeaction: true,
41742         /**
41743          * @event actionfailed
41744          * Fires when an action fails.
41745          * @param {Form} this
41746          * @param {Action} action The action that failed
41747          */
41748         actionfailed : true,
41749         /**
41750          * @event actioncomplete
41751          * Fires when an action is completed.
41752          * @param {Form} this
41753          * @param {Action} action The action that completed
41754          */
41755         actioncomplete : true
41756     });
41757     if(el){
41758         this.initEl(el);
41759     }
41760     Roo.form.BasicForm.superclass.constructor.call(this);
41761 };
41762
41763 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41764     /**
41765      * @cfg {String} method
41766      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41767      */
41768     /**
41769      * @cfg {DataReader} reader
41770      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41771      * This is optional as there is built-in support for processing JSON.
41772      */
41773     /**
41774      * @cfg {DataReader} errorReader
41775      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41776      * This is completely optional as there is built-in support for processing JSON.
41777      */
41778     /**
41779      * @cfg {String} url
41780      * The URL to use for form actions if one isn't supplied in the action options.
41781      */
41782     /**
41783      * @cfg {Boolean} fileUpload
41784      * Set to true if this form is a file upload.
41785      */
41786      
41787     /**
41788      * @cfg {Object} baseParams
41789      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41790      */
41791      /**
41792      
41793     /**
41794      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41795      */
41796     timeout: 30,
41797
41798     // private
41799     activeAction : null,
41800
41801     /**
41802      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41803      * or setValues() data instead of when the form was first created.
41804      */
41805     trackResetOnLoad : false,
41806     
41807     
41808     /**
41809      * childForms - used for multi-tab forms
41810      * @type {Array}
41811      */
41812     childForms : false,
41813     
41814     /**
41815      * allItems - full list of fields.
41816      * @type {Array}
41817      */
41818     allItems : false,
41819     
41820     /**
41821      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41822      * element by passing it or its id or mask the form itself by passing in true.
41823      * @type Mixed
41824      */
41825     waitMsgTarget : false,
41826
41827     // private
41828     initEl : function(el){
41829         this.el = Roo.get(el);
41830         this.id = this.el.id || Roo.id();
41831         this.el.on('submit', this.onSubmit, this);
41832         this.el.addClass('x-form');
41833     },
41834
41835     // private
41836     onSubmit : function(e){
41837         e.stopEvent();
41838     },
41839
41840     /**
41841      * Returns true if client-side validation on the form is successful.
41842      * @return Boolean
41843      */
41844     isValid : function(){
41845         var valid = true;
41846         this.items.each(function(f){
41847            if(!f.validate()){
41848                valid = false;
41849            }
41850         });
41851         return valid;
41852     },
41853
41854     /**
41855      * Returns true if any fields in this form have changed since their original load.
41856      * @return Boolean
41857      */
41858     isDirty : function(){
41859         var dirty = false;
41860         this.items.each(function(f){
41861            if(f.isDirty()){
41862                dirty = true;
41863                return false;
41864            }
41865         });
41866         return dirty;
41867     },
41868
41869     /**
41870      * Performs a predefined action (submit or load) or custom actions you define on this form.
41871      * @param {String} actionName The name of the action type
41872      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41873      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41874      * accept other config options):
41875      * <pre>
41876 Property          Type             Description
41877 ----------------  ---------------  ----------------------------------------------------------------------------------
41878 url               String           The url for the action (defaults to the form's url)
41879 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41880 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41881 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41882                                    validate the form on the client (defaults to false)
41883      * </pre>
41884      * @return {BasicForm} this
41885      */
41886     doAction : function(action, options){
41887         if(typeof action == 'string'){
41888             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41889         }
41890         if(this.fireEvent('beforeaction', this, action) !== false){
41891             this.beforeAction(action);
41892             action.run.defer(100, action);
41893         }
41894         return this;
41895     },
41896
41897     /**
41898      * Shortcut to do a submit action.
41899      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41900      * @return {BasicForm} this
41901      */
41902     submit : function(options){
41903         this.doAction('submit', options);
41904         return this;
41905     },
41906
41907     /**
41908      * Shortcut to do a load action.
41909      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41910      * @return {BasicForm} this
41911      */
41912     load : function(options){
41913         this.doAction('load', options);
41914         return this;
41915     },
41916
41917     /**
41918      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41919      * @param {Record} record The record to edit
41920      * @return {BasicForm} this
41921      */
41922     updateRecord : function(record){
41923         record.beginEdit();
41924         var fs = record.fields;
41925         fs.each(function(f){
41926             var field = this.findField(f.name);
41927             if(field){
41928                 record.set(f.name, field.getValue());
41929             }
41930         }, this);
41931         record.endEdit();
41932         return this;
41933     },
41934
41935     /**
41936      * Loads an Roo.data.Record into this form.
41937      * @param {Record} record The record to load
41938      * @return {BasicForm} this
41939      */
41940     loadRecord : function(record){
41941         this.setValues(record.data);
41942         return this;
41943     },
41944
41945     // private
41946     beforeAction : function(action){
41947         var o = action.options;
41948         
41949        
41950         if(this.waitMsgTarget === true){
41951             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41952         }else if(this.waitMsgTarget){
41953             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41954             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41955         }else {
41956             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41957         }
41958          
41959     },
41960
41961     // private
41962     afterAction : function(action, success){
41963         this.activeAction = null;
41964         var o = action.options;
41965         
41966         if(this.waitMsgTarget === true){
41967             this.el.unmask();
41968         }else if(this.waitMsgTarget){
41969             this.waitMsgTarget.unmask();
41970         }else{
41971             Roo.MessageBox.updateProgress(1);
41972             Roo.MessageBox.hide();
41973         }
41974          
41975         if(success){
41976             if(o.reset){
41977                 this.reset();
41978             }
41979             Roo.callback(o.success, o.scope, [this, action]);
41980             this.fireEvent('actioncomplete', this, action);
41981             
41982         }else{
41983             
41984             // failure condition..
41985             // we have a scenario where updates need confirming.
41986             // eg. if a locking scenario exists..
41987             // we look for { errors : { needs_confirm : true }} in the response.
41988             if (
41989                 (typeof(action.result) != 'undefined')  &&
41990                 (typeof(action.result.errors) != 'undefined')  &&
41991                 (typeof(action.result.errors.needs_confirm) != 'undefined')
41992           ){
41993                 var _t = this;
41994                 Roo.MessageBox.confirm(
41995                     "Change requires confirmation",
41996                     action.result.errorMsg,
41997                     function(r) {
41998                         if (r != 'yes') {
41999                             return;
42000                         }
42001                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
42002                     }
42003                     
42004                 );
42005                 
42006                 
42007                 
42008                 return;
42009             }
42010             
42011             Roo.callback(o.failure, o.scope, [this, action]);
42012             // show an error message if no failed handler is set..
42013             if (!this.hasListener('actionfailed')) {
42014                 Roo.MessageBox.alert("Error",
42015                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
42016                         action.result.errorMsg :
42017                         "Saving Failed, please check your entries or try again"
42018                 );
42019             }
42020             
42021             this.fireEvent('actionfailed', this, action);
42022         }
42023         
42024     },
42025
42026     /**
42027      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
42028      * @param {String} id The value to search for
42029      * @return Field
42030      */
42031     findField : function(id){
42032         var field = this.items.get(id);
42033         if(!field){
42034             this.items.each(function(f){
42035                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
42036                     field = f;
42037                     return false;
42038                 }
42039             });
42040         }
42041         return field || null;
42042     },
42043
42044     /**
42045      * Add a secondary form to this one, 
42046      * Used to provide tabbed forms. One form is primary, with hidden values 
42047      * which mirror the elements from the other forms.
42048      * 
42049      * @param {Roo.form.Form} form to add.
42050      * 
42051      */
42052     addForm : function(form)
42053     {
42054        
42055         if (this.childForms.indexOf(form) > -1) {
42056             // already added..
42057             return;
42058         }
42059         this.childForms.push(form);
42060         var n = '';
42061         Roo.each(form.allItems, function (fe) {
42062             
42063             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
42064             if (this.findField(n)) { // already added..
42065                 return;
42066             }
42067             var add = new Roo.form.Hidden({
42068                 name : n
42069             });
42070             add.render(this.el);
42071             
42072             this.add( add );
42073         }, this);
42074         
42075     },
42076     /**
42077      * Mark fields in this form invalid in bulk.
42078      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
42079      * @return {BasicForm} this
42080      */
42081     markInvalid : function(errors){
42082         if(errors instanceof Array){
42083             for(var i = 0, len = errors.length; i < len; i++){
42084                 var fieldError = errors[i];
42085                 var f = this.findField(fieldError.id);
42086                 if(f){
42087                     f.markInvalid(fieldError.msg);
42088                 }
42089             }
42090         }else{
42091             var field, id;
42092             for(id in errors){
42093                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
42094                     field.markInvalid(errors[id]);
42095                 }
42096             }
42097         }
42098         Roo.each(this.childForms || [], function (f) {
42099             f.markInvalid(errors);
42100         });
42101         
42102         return this;
42103     },
42104
42105     /**
42106      * Set values for fields in this form in bulk.
42107      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
42108      * @return {BasicForm} this
42109      */
42110     setValues : function(values){
42111         if(values instanceof Array){ // array of objects
42112             for(var i = 0, len = values.length; i < len; i++){
42113                 var v = values[i];
42114                 var f = this.findField(v.id);
42115                 if(f){
42116                     f.setValue(v.value);
42117                     if(this.trackResetOnLoad){
42118                         f.originalValue = f.getValue();
42119                     }
42120                 }
42121             }
42122         }else{ // object hash
42123             var field, id;
42124             for(id in values){
42125                 if(typeof values[id] != 'function' && (field = this.findField(id))){
42126                     
42127                     if (field.setFromData && 
42128                         field.valueField && 
42129                         field.displayField &&
42130                         // combos' with local stores can 
42131                         // be queried via setValue()
42132                         // to set their value..
42133                         (field.store && !field.store.isLocal)
42134                         ) {
42135                         // it's a combo
42136                         var sd = { };
42137                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
42138                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
42139                         field.setFromData(sd);
42140                         
42141                     } else {
42142                         field.setValue(values[id]);
42143                     }
42144                     
42145                     
42146                     if(this.trackResetOnLoad){
42147                         field.originalValue = field.getValue();
42148                     }
42149                 }
42150             }
42151         }
42152          
42153         Roo.each(this.childForms || [], function (f) {
42154             f.setValues(values);
42155         });
42156                 
42157         return this;
42158     },
42159
42160     /**
42161      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
42162      * they are returned as an array.
42163      * @param {Boolean} asString
42164      * @return {Object}
42165      */
42166     getValues : function(asString){
42167         if (this.childForms) {
42168             // copy values from the child forms
42169             Roo.each(this.childForms, function (f) {
42170                 this.setValues(f.getValues());
42171             }, this);
42172         }
42173         
42174         
42175         
42176         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
42177         if(asString === true){
42178             return fs;
42179         }
42180         return Roo.urlDecode(fs);
42181     },
42182     
42183     /**
42184      * Returns the fields in this form as an object with key/value pairs. 
42185      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
42186      * @return {Object}
42187      */
42188     getFieldValues : function(with_hidden)
42189     {
42190         if (this.childForms) {
42191             // copy values from the child forms
42192             // should this call getFieldValues - probably not as we do not currently copy
42193             // hidden fields when we generate..
42194             Roo.each(this.childForms, function (f) {
42195                 this.setValues(f.getValues());
42196             }, this);
42197         }
42198         
42199         var ret = {};
42200         this.items.each(function(f){
42201             if (!f.getName()) {
42202                 return;
42203             }
42204             var v = f.getValue();
42205             // not sure if this supported any more..
42206             if ((typeof(v) == 'object') && f.getRawValue) {
42207                 v = f.getRawValue() ; // dates..
42208             }
42209             // combo boxes where name != hiddenName...
42210             if (f.name != f.getName()) {
42211                 ret[f.name] = f.getRawValue();
42212             }
42213             ret[f.getName()] = v;
42214         });
42215         
42216         return ret;
42217     },
42218
42219     /**
42220      * Clears all invalid messages in this form.
42221      * @return {BasicForm} this
42222      */
42223     clearInvalid : function(){
42224         this.items.each(function(f){
42225            f.clearInvalid();
42226         });
42227         
42228         Roo.each(this.childForms || [], function (f) {
42229             f.clearInvalid();
42230         });
42231         
42232         
42233         return this;
42234     },
42235
42236     /**
42237      * Resets this form.
42238      * @return {BasicForm} this
42239      */
42240     reset : function(){
42241         this.items.each(function(f){
42242             f.reset();
42243         });
42244         
42245         Roo.each(this.childForms || [], function (f) {
42246             f.reset();
42247         });
42248        
42249         
42250         return this;
42251     },
42252
42253     /**
42254      * Add Roo.form components to this form.
42255      * @param {Field} field1
42256      * @param {Field} field2 (optional)
42257      * @param {Field} etc (optional)
42258      * @return {BasicForm} this
42259      */
42260     add : function(){
42261         this.items.addAll(Array.prototype.slice.call(arguments, 0));
42262         return this;
42263     },
42264
42265
42266     /**
42267      * Removes a field from the items collection (does NOT remove its markup).
42268      * @param {Field} field
42269      * @return {BasicForm} this
42270      */
42271     remove : function(field){
42272         this.items.remove(field);
42273         return this;
42274     },
42275
42276     /**
42277      * Looks at the fields in this form, checks them for an id attribute,
42278      * and calls applyTo on the existing dom element with that id.
42279      * @return {BasicForm} this
42280      */
42281     render : function(){
42282         this.items.each(function(f){
42283             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
42284                 f.applyTo(f.id);
42285             }
42286         });
42287         return this;
42288     },
42289
42290     /**
42291      * Calls {@link Ext#apply} for all fields in this form with the passed object.
42292      * @param {Object} values
42293      * @return {BasicForm} this
42294      */
42295     applyToFields : function(o){
42296         this.items.each(function(f){
42297            Roo.apply(f, o);
42298         });
42299         return this;
42300     },
42301
42302     /**
42303      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
42304      * @param {Object} values
42305      * @return {BasicForm} this
42306      */
42307     applyIfToFields : function(o){
42308         this.items.each(function(f){
42309            Roo.applyIf(f, o);
42310         });
42311         return this;
42312     }
42313 });
42314
42315 // back compat
42316 Roo.BasicForm = Roo.form.BasicForm;/*
42317  * Based on:
42318  * Ext JS Library 1.1.1
42319  * Copyright(c) 2006-2007, Ext JS, LLC.
42320  *
42321  * Originally Released Under LGPL - original licence link has changed is not relivant.
42322  *
42323  * Fork - LGPL
42324  * <script type="text/javascript">
42325  */
42326
42327 /**
42328  * @class Roo.form.Form
42329  * @extends Roo.form.BasicForm
42330  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
42331  * @constructor
42332  * @param {Object} config Configuration options
42333  */
42334 Roo.form.Form = function(config){
42335     var xitems =  [];
42336     if (config.items) {
42337         xitems = config.items;
42338         delete config.items;
42339     }
42340    
42341     
42342     Roo.form.Form.superclass.constructor.call(this, null, config);
42343     this.url = this.url || this.action;
42344     if(!this.root){
42345         this.root = new Roo.form.Layout(Roo.applyIf({
42346             id: Roo.id()
42347         }, config));
42348     }
42349     this.active = this.root;
42350     /**
42351      * Array of all the buttons that have been added to this form via {@link addButton}
42352      * @type Array
42353      */
42354     this.buttons = [];
42355     this.allItems = [];
42356     this.addEvents({
42357         /**
42358          * @event clientvalidation
42359          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
42360          * @param {Form} this
42361          * @param {Boolean} valid true if the form has passed client-side validation
42362          */
42363         clientvalidation: true,
42364         /**
42365          * @event rendered
42366          * Fires when the form is rendered
42367          * @param {Roo.form.Form} form
42368          */
42369         rendered : true
42370     });
42371     
42372     if (this.progressUrl) {
42373             // push a hidden field onto the list of fields..
42374             this.addxtype( {
42375                     xns: Roo.form, 
42376                     xtype : 'Hidden', 
42377                     name : 'UPLOAD_IDENTIFIER' 
42378             });
42379         }
42380         
42381     
42382     Roo.each(xitems, this.addxtype, this);
42383     
42384     
42385     
42386 };
42387
42388 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
42389     /**
42390      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
42391      */
42392     /**
42393      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
42394      */
42395     /**
42396      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
42397      */
42398     buttonAlign:'center',
42399
42400     /**
42401      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
42402      */
42403     minButtonWidth:75,
42404
42405     /**
42406      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
42407      * This property cascades to child containers if not set.
42408      */
42409     labelAlign:'left',
42410
42411     /**
42412      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
42413      * fires a looping event with that state. This is required to bind buttons to the valid
42414      * state using the config value formBind:true on the button.
42415      */
42416     monitorValid : false,
42417
42418     /**
42419      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
42420      */
42421     monitorPoll : 200,
42422     
42423     /**
42424      * @cfg {String} progressUrl - Url to return progress data 
42425      */
42426     
42427     progressUrl : false,
42428   
42429     /**
42430      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
42431      * fields are added and the column is closed. If no fields are passed the column remains open
42432      * until end() is called.
42433      * @param {Object} config The config to pass to the column
42434      * @param {Field} field1 (optional)
42435      * @param {Field} field2 (optional)
42436      * @param {Field} etc (optional)
42437      * @return Column The column container object
42438      */
42439     column : function(c){
42440         var col = new Roo.form.Column(c);
42441         this.start(col);
42442         if(arguments.length > 1){ // duplicate code required because of Opera
42443             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42444             this.end();
42445         }
42446         return col;
42447     },
42448
42449     /**
42450      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
42451      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
42452      * until end() is called.
42453      * @param {Object} config The config to pass to the fieldset
42454      * @param {Field} field1 (optional)
42455      * @param {Field} field2 (optional)
42456      * @param {Field} etc (optional)
42457      * @return FieldSet The fieldset container object
42458      */
42459     fieldset : function(c){
42460         var fs = new Roo.form.FieldSet(c);
42461         this.start(fs);
42462         if(arguments.length > 1){ // duplicate code required because of Opera
42463             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42464             this.end();
42465         }
42466         return fs;
42467     },
42468
42469     /**
42470      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
42471      * fields are added and the container is closed. If no fields are passed the container remains open
42472      * until end() is called.
42473      * @param {Object} config The config to pass to the Layout
42474      * @param {Field} field1 (optional)
42475      * @param {Field} field2 (optional)
42476      * @param {Field} etc (optional)
42477      * @return Layout The container object
42478      */
42479     container : function(c){
42480         var l = new Roo.form.Layout(c);
42481         this.start(l);
42482         if(arguments.length > 1){ // duplicate code required because of Opera
42483             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42484             this.end();
42485         }
42486         return l;
42487     },
42488
42489     /**
42490      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
42491      * @param {Object} container A Roo.form.Layout or subclass of Layout
42492      * @return {Form} this
42493      */
42494     start : function(c){
42495         // cascade label info
42496         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
42497         this.active.stack.push(c);
42498         c.ownerCt = this.active;
42499         this.active = c;
42500         return this;
42501     },
42502
42503     /**
42504      * Closes the current open container
42505      * @return {Form} this
42506      */
42507     end : function(){
42508         if(this.active == this.root){
42509             return this;
42510         }
42511         this.active = this.active.ownerCt;
42512         return this;
42513     },
42514
42515     /**
42516      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
42517      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
42518      * as the label of the field.
42519      * @param {Field} field1
42520      * @param {Field} field2 (optional)
42521      * @param {Field} etc. (optional)
42522      * @return {Form} this
42523      */
42524     add : function(){
42525         this.active.stack.push.apply(this.active.stack, arguments);
42526         this.allItems.push.apply(this.allItems,arguments);
42527         var r = [];
42528         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
42529             if(a[i].isFormField){
42530                 r.push(a[i]);
42531             }
42532         }
42533         if(r.length > 0){
42534             Roo.form.Form.superclass.add.apply(this, r);
42535         }
42536         return this;
42537     },
42538     
42539
42540     
42541     
42542     
42543      /**
42544      * Find any element that has been added to a form, using it's ID or name
42545      * This can include framesets, columns etc. along with regular fields..
42546      * @param {String} id - id or name to find.
42547      
42548      * @return {Element} e - or false if nothing found.
42549      */
42550     findbyId : function(id)
42551     {
42552         var ret = false;
42553         if (!id) {
42554             return ret;
42555         }
42556         Roo.each(this.allItems, function(f){
42557             if (f.id == id || f.name == id ){
42558                 ret = f;
42559                 return false;
42560             }
42561         });
42562         return ret;
42563     },
42564
42565     
42566     
42567     /**
42568      * Render this form into the passed container. This should only be called once!
42569      * @param {String/HTMLElement/Element} container The element this component should be rendered into
42570      * @return {Form} this
42571      */
42572     render : function(ct)
42573     {
42574         
42575         
42576         
42577         ct = Roo.get(ct);
42578         var o = this.autoCreate || {
42579             tag: 'form',
42580             method : this.method || 'POST',
42581             id : this.id || Roo.id()
42582         };
42583         this.initEl(ct.createChild(o));
42584
42585         this.root.render(this.el);
42586         
42587        
42588              
42589         this.items.each(function(f){
42590             f.render('x-form-el-'+f.id);
42591         });
42592
42593         if(this.buttons.length > 0){
42594             // tables are required to maintain order and for correct IE layout
42595             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
42596                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
42597                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
42598             }}, null, true);
42599             var tr = tb.getElementsByTagName('tr')[0];
42600             for(var i = 0, len = this.buttons.length; i < len; i++) {
42601                 var b = this.buttons[i];
42602                 var td = document.createElement('td');
42603                 td.className = 'x-form-btn-td';
42604                 b.render(tr.appendChild(td));
42605             }
42606         }
42607         if(this.monitorValid){ // initialize after render
42608             this.startMonitoring();
42609         }
42610         this.fireEvent('rendered', this);
42611         return this;
42612     },
42613
42614     /**
42615      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42616      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42617      * object or a valid Roo.DomHelper element config
42618      * @param {Function} handler The function called when the button is clicked
42619      * @param {Object} scope (optional) The scope of the handler function
42620      * @return {Roo.Button}
42621      */
42622     addButton : function(config, handler, scope){
42623         var bc = {
42624             handler: handler,
42625             scope: scope,
42626             minWidth: this.minButtonWidth,
42627             hideParent:true
42628         };
42629         if(typeof config == "string"){
42630             bc.text = config;
42631         }else{
42632             Roo.apply(bc, config);
42633         }
42634         var btn = new Roo.Button(null, bc);
42635         this.buttons.push(btn);
42636         return btn;
42637     },
42638
42639      /**
42640      * Adds a series of form elements (using the xtype property as the factory method.
42641      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42642      * @param {Object} config 
42643      */
42644     
42645     addxtype : function()
42646     {
42647         var ar = Array.prototype.slice.call(arguments, 0);
42648         var ret = false;
42649         for(var i = 0; i < ar.length; i++) {
42650             if (!ar[i]) {
42651                 continue; // skip -- if this happends something invalid got sent, we 
42652                 // should ignore it, as basically that interface element will not show up
42653                 // and that should be pretty obvious!!
42654             }
42655             
42656             if (Roo.form[ar[i].xtype]) {
42657                 ar[i].form = this;
42658                 var fe = Roo.factory(ar[i], Roo.form);
42659                 if (!ret) {
42660                     ret = fe;
42661                 }
42662                 fe.form = this;
42663                 if (fe.store) {
42664                     fe.store.form = this;
42665                 }
42666                 if (fe.isLayout) {  
42667                          
42668                     this.start(fe);
42669                     this.allItems.push(fe);
42670                     if (fe.items && fe.addxtype) {
42671                         fe.addxtype.apply(fe, fe.items);
42672                         delete fe.items;
42673                     }
42674                      this.end();
42675                     continue;
42676                 }
42677                 
42678                 
42679                  
42680                 this.add(fe);
42681               //  console.log('adding ' + ar[i].xtype);
42682             }
42683             if (ar[i].xtype == 'Button') {  
42684                 //console.log('adding button');
42685                 //console.log(ar[i]);
42686                 this.addButton(ar[i]);
42687                 this.allItems.push(fe);
42688                 continue;
42689             }
42690             
42691             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42692                 alert('end is not supported on xtype any more, use items');
42693             //    this.end();
42694             //    //console.log('adding end');
42695             }
42696             
42697         }
42698         return ret;
42699     },
42700     
42701     /**
42702      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42703      * option "monitorValid"
42704      */
42705     startMonitoring : function(){
42706         if(!this.bound){
42707             this.bound = true;
42708             Roo.TaskMgr.start({
42709                 run : this.bindHandler,
42710                 interval : this.monitorPoll || 200,
42711                 scope: this
42712             });
42713         }
42714     },
42715
42716     /**
42717      * Stops monitoring of the valid state of this form
42718      */
42719     stopMonitoring : function(){
42720         this.bound = false;
42721     },
42722
42723     // private
42724     bindHandler : function(){
42725         if(!this.bound){
42726             return false; // stops binding
42727         }
42728         var valid = true;
42729         this.items.each(function(f){
42730             if(!f.isValid(true)){
42731                 valid = false;
42732                 return false;
42733             }
42734         });
42735         for(var i = 0, len = this.buttons.length; i < len; i++){
42736             var btn = this.buttons[i];
42737             if(btn.formBind === true && btn.disabled === valid){
42738                 btn.setDisabled(!valid);
42739             }
42740         }
42741         this.fireEvent('clientvalidation', this, valid);
42742     }
42743     
42744     
42745     
42746     
42747     
42748     
42749     
42750     
42751 });
42752
42753
42754 // back compat
42755 Roo.Form = Roo.form.Form;
42756 /*
42757  * Based on:
42758  * Ext JS Library 1.1.1
42759  * Copyright(c) 2006-2007, Ext JS, LLC.
42760  *
42761  * Originally Released Under LGPL - original licence link has changed is not relivant.
42762  *
42763  * Fork - LGPL
42764  * <script type="text/javascript">
42765  */
42766  
42767  /**
42768  * @class Roo.form.Action
42769  * Internal Class used to handle form actions
42770  * @constructor
42771  * @param {Roo.form.BasicForm} el The form element or its id
42772  * @param {Object} config Configuration options
42773  */
42774  
42775  
42776 // define the action interface
42777 Roo.form.Action = function(form, options){
42778     this.form = form;
42779     this.options = options || {};
42780 };
42781 /**
42782  * Client Validation Failed
42783  * @const 
42784  */
42785 Roo.form.Action.CLIENT_INVALID = 'client';
42786 /**
42787  * Server Validation Failed
42788  * @const 
42789  */
42790  Roo.form.Action.SERVER_INVALID = 'server';
42791  /**
42792  * Connect to Server Failed
42793  * @const 
42794  */
42795 Roo.form.Action.CONNECT_FAILURE = 'connect';
42796 /**
42797  * Reading Data from Server Failed
42798  * @const 
42799  */
42800 Roo.form.Action.LOAD_FAILURE = 'load';
42801
42802 Roo.form.Action.prototype = {
42803     type : 'default',
42804     failureType : undefined,
42805     response : undefined,
42806     result : undefined,
42807
42808     // interface method
42809     run : function(options){
42810
42811     },
42812
42813     // interface method
42814     success : function(response){
42815
42816     },
42817
42818     // interface method
42819     handleResponse : function(response){
42820
42821     },
42822
42823     // default connection failure
42824     failure : function(response){
42825         
42826         this.response = response;
42827         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42828         this.form.afterAction(this, false);
42829     },
42830
42831     processResponse : function(response){
42832         this.response = response;
42833         if(!response.responseText){
42834             return true;
42835         }
42836         this.result = this.handleResponse(response);
42837         return this.result;
42838     },
42839
42840     // utility functions used internally
42841     getUrl : function(appendParams){
42842         var url = this.options.url || this.form.url || this.form.el.dom.action;
42843         if(appendParams){
42844             var p = this.getParams();
42845             if(p){
42846                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42847             }
42848         }
42849         return url;
42850     },
42851
42852     getMethod : function(){
42853         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42854     },
42855
42856     getParams : function(){
42857         var bp = this.form.baseParams;
42858         var p = this.options.params;
42859         if(p){
42860             if(typeof p == "object"){
42861                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42862             }else if(typeof p == 'string' && bp){
42863                 p += '&' + Roo.urlEncode(bp);
42864             }
42865         }else if(bp){
42866             p = Roo.urlEncode(bp);
42867         }
42868         return p;
42869     },
42870
42871     createCallback : function(){
42872         return {
42873             success: this.success,
42874             failure: this.failure,
42875             scope: this,
42876             timeout: (this.form.timeout*1000),
42877             upload: this.form.fileUpload ? this.success : undefined
42878         };
42879     }
42880 };
42881
42882 Roo.form.Action.Submit = function(form, options){
42883     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42884 };
42885
42886 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42887     type : 'submit',
42888
42889     haveProgress : false,
42890     uploadComplete : false,
42891     
42892     // uploadProgress indicator.
42893     uploadProgress : function()
42894     {
42895         if (!this.form.progressUrl) {
42896             return;
42897         }
42898         
42899         if (!this.haveProgress) {
42900             Roo.MessageBox.progress("Uploading", "Uploading");
42901         }
42902         if (this.uploadComplete) {
42903            Roo.MessageBox.hide();
42904            return;
42905         }
42906         
42907         this.haveProgress = true;
42908    
42909         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42910         
42911         var c = new Roo.data.Connection();
42912         c.request({
42913             url : this.form.progressUrl,
42914             params: {
42915                 id : uid
42916             },
42917             method: 'GET',
42918             success : function(req){
42919                //console.log(data);
42920                 var rdata = false;
42921                 var edata;
42922                 try  {
42923                    rdata = Roo.decode(req.responseText)
42924                 } catch (e) {
42925                     Roo.log("Invalid data from server..");
42926                     Roo.log(edata);
42927                     return;
42928                 }
42929                 if (!rdata || !rdata.success) {
42930                     Roo.log(rdata);
42931                     return;
42932                 }
42933                 var data = rdata.data;
42934                 
42935                 if (this.uploadComplete) {
42936                    Roo.MessageBox.hide();
42937                    return;
42938                 }
42939                    
42940                 if (data){
42941                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42942                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42943                     );
42944                 }
42945                 this.uploadProgress.defer(2000,this);
42946             },
42947        
42948             failure: function(data) {
42949                 Roo.log('progress url failed ');
42950                 Roo.log(data);
42951             },
42952             scope : this
42953         });
42954            
42955     },
42956     
42957     
42958     run : function()
42959     {
42960         // run get Values on the form, so it syncs any secondary forms.
42961         this.form.getValues();
42962         
42963         var o = this.options;
42964         var method = this.getMethod();
42965         var isPost = method == 'POST';
42966         if(o.clientValidation === false || this.form.isValid()){
42967             
42968             if (this.form.progressUrl) {
42969                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42970                     (new Date() * 1) + '' + Math.random());
42971                     
42972             } 
42973             
42974             
42975             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42976                 form:this.form.el.dom,
42977                 url:this.getUrl(!isPost),
42978                 method: method,
42979                 params:isPost ? this.getParams() : null,
42980                 isUpload: this.form.fileUpload
42981             }));
42982             
42983             this.uploadProgress();
42984
42985         }else if (o.clientValidation !== false){ // client validation failed
42986             this.failureType = Roo.form.Action.CLIENT_INVALID;
42987             this.form.afterAction(this, false);
42988         }
42989     },
42990
42991     success : function(response)
42992     {
42993         this.uploadComplete= true;
42994         if (this.haveProgress) {
42995             Roo.MessageBox.hide();
42996         }
42997         
42998         
42999         var result = this.processResponse(response);
43000         if(result === true || result.success){
43001             this.form.afterAction(this, true);
43002             return;
43003         }
43004         if(result.errors){
43005             this.form.markInvalid(result.errors);
43006             this.failureType = Roo.form.Action.SERVER_INVALID;
43007         }
43008         this.form.afterAction(this, false);
43009     },
43010     failure : function(response)
43011     {
43012         this.uploadComplete= true;
43013         if (this.haveProgress) {
43014             Roo.MessageBox.hide();
43015         }
43016         
43017         this.response = response;
43018         this.failureType = Roo.form.Action.CONNECT_FAILURE;
43019         this.form.afterAction(this, false);
43020     },
43021     
43022     handleResponse : function(response){
43023         if(this.form.errorReader){
43024             var rs = this.form.errorReader.read(response);
43025             var errors = [];
43026             if(rs.records){
43027                 for(var i = 0, len = rs.records.length; i < len; i++) {
43028                     var r = rs.records[i];
43029                     errors[i] = r.data;
43030                 }
43031             }
43032             if(errors.length < 1){
43033                 errors = null;
43034             }
43035             return {
43036                 success : rs.success,
43037                 errors : errors
43038             };
43039         }
43040         var ret = false;
43041         try {
43042             ret = Roo.decode(response.responseText);
43043         } catch (e) {
43044             ret = {
43045                 success: false,
43046                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
43047                 errors : []
43048             };
43049         }
43050         return ret;
43051         
43052     }
43053 });
43054
43055
43056 Roo.form.Action.Load = function(form, options){
43057     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
43058     this.reader = this.form.reader;
43059 };
43060
43061 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
43062     type : 'load',
43063
43064     run : function(){
43065         
43066         Roo.Ajax.request(Roo.apply(
43067                 this.createCallback(), {
43068                     method:this.getMethod(),
43069                     url:this.getUrl(false),
43070                     params:this.getParams()
43071         }));
43072     },
43073
43074     success : function(response){
43075         
43076         var result = this.processResponse(response);
43077         if(result === true || !result.success || !result.data){
43078             this.failureType = Roo.form.Action.LOAD_FAILURE;
43079             this.form.afterAction(this, false);
43080             return;
43081         }
43082         this.form.clearInvalid();
43083         this.form.setValues(result.data);
43084         this.form.afterAction(this, true);
43085     },
43086
43087     handleResponse : function(response){
43088         if(this.form.reader){
43089             var rs = this.form.reader.read(response);
43090             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
43091             return {
43092                 success : rs.success,
43093                 data : data
43094             };
43095         }
43096         return Roo.decode(response.responseText);
43097     }
43098 });
43099
43100 Roo.form.Action.ACTION_TYPES = {
43101     'load' : Roo.form.Action.Load,
43102     'submit' : Roo.form.Action.Submit
43103 };/*
43104  * Based on:
43105  * Ext JS Library 1.1.1
43106  * Copyright(c) 2006-2007, Ext JS, LLC.
43107  *
43108  * Originally Released Under LGPL - original licence link has changed is not relivant.
43109  *
43110  * Fork - LGPL
43111  * <script type="text/javascript">
43112  */
43113  
43114 /**
43115  * @class Roo.form.Layout
43116  * @extends Roo.Component
43117  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
43118  * @constructor
43119  * @param {Object} config Configuration options
43120  */
43121 Roo.form.Layout = function(config){
43122     var xitems = [];
43123     if (config.items) {
43124         xitems = config.items;
43125         delete config.items;
43126     }
43127     Roo.form.Layout.superclass.constructor.call(this, config);
43128     this.stack = [];
43129     Roo.each(xitems, this.addxtype, this);
43130      
43131 };
43132
43133 Roo.extend(Roo.form.Layout, Roo.Component, {
43134     /**
43135      * @cfg {String/Object} autoCreate
43136      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
43137      */
43138     /**
43139      * @cfg {String/Object/Function} style
43140      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
43141      * a function which returns such a specification.
43142      */
43143     /**
43144      * @cfg {String} labelAlign
43145      * Valid values are "left," "top" and "right" (defaults to "left")
43146      */
43147     /**
43148      * @cfg {Number} labelWidth
43149      * Fixed width in pixels of all field labels (defaults to undefined)
43150      */
43151     /**
43152      * @cfg {Boolean} clear
43153      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
43154      */
43155     clear : true,
43156     /**
43157      * @cfg {String} labelSeparator
43158      * The separator to use after field labels (defaults to ':')
43159      */
43160     labelSeparator : ':',
43161     /**
43162      * @cfg {Boolean} hideLabels
43163      * True to suppress the display of field labels in this layout (defaults to false)
43164      */
43165     hideLabels : false,
43166
43167     // private
43168     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
43169     
43170     isLayout : true,
43171     
43172     // private
43173     onRender : function(ct, position){
43174         if(this.el){ // from markup
43175             this.el = Roo.get(this.el);
43176         }else {  // generate
43177             var cfg = this.getAutoCreate();
43178             this.el = ct.createChild(cfg, position);
43179         }
43180         if(this.style){
43181             this.el.applyStyles(this.style);
43182         }
43183         if(this.labelAlign){
43184             this.el.addClass('x-form-label-'+this.labelAlign);
43185         }
43186         if(this.hideLabels){
43187             this.labelStyle = "display:none";
43188             this.elementStyle = "padding-left:0;";
43189         }else{
43190             if(typeof this.labelWidth == 'number'){
43191                 this.labelStyle = "width:"+this.labelWidth+"px;";
43192                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
43193             }
43194             if(this.labelAlign == 'top'){
43195                 this.labelStyle = "width:auto;";
43196                 this.elementStyle = "padding-left:0;";
43197             }
43198         }
43199         var stack = this.stack;
43200         var slen = stack.length;
43201         if(slen > 0){
43202             if(!this.fieldTpl){
43203                 var t = new Roo.Template(
43204                     '<div class="x-form-item {5}">',
43205                         '<label for="{0}" style="{2}">{1}{4}</label>',
43206                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
43207                         '</div>',
43208                     '</div><div class="x-form-clear-left"></div>'
43209                 );
43210                 t.disableFormats = true;
43211                 t.compile();
43212                 Roo.form.Layout.prototype.fieldTpl = t;
43213             }
43214             for(var i = 0; i < slen; i++) {
43215                 if(stack[i].isFormField){
43216                     this.renderField(stack[i]);
43217                 }else{
43218                     this.renderComponent(stack[i]);
43219                 }
43220             }
43221         }
43222         if(this.clear){
43223             this.el.createChild({cls:'x-form-clear'});
43224         }
43225     },
43226
43227     // private
43228     renderField : function(f){
43229         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
43230                f.id, //0
43231                f.fieldLabel, //1
43232                f.labelStyle||this.labelStyle||'', //2
43233                this.elementStyle||'', //3
43234                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
43235                f.itemCls||this.itemCls||''  //5
43236        ], true).getPrevSibling());
43237     },
43238
43239     // private
43240     renderComponent : function(c){
43241         c.render(c.isLayout ? this.el : this.el.createChild());    
43242     },
43243     /**
43244      * Adds a object form elements (using the xtype property as the factory method.)
43245      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
43246      * @param {Object} config 
43247      */
43248     addxtype : function(o)
43249     {
43250         // create the lement.
43251         o.form = this.form;
43252         var fe = Roo.factory(o, Roo.form);
43253         this.form.allItems.push(fe);
43254         this.stack.push(fe);
43255         
43256         if (fe.isFormField) {
43257             this.form.items.add(fe);
43258         }
43259          
43260         return fe;
43261     }
43262 });
43263
43264 /**
43265  * @class Roo.form.Column
43266  * @extends Roo.form.Layout
43267  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
43268  * @constructor
43269  * @param {Object} config Configuration options
43270  */
43271 Roo.form.Column = function(config){
43272     Roo.form.Column.superclass.constructor.call(this, config);
43273 };
43274
43275 Roo.extend(Roo.form.Column, Roo.form.Layout, {
43276     /**
43277      * @cfg {Number/String} width
43278      * The fixed width of the column in pixels or CSS value (defaults to "auto")
43279      */
43280     /**
43281      * @cfg {String/Object} autoCreate
43282      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
43283      */
43284
43285     // private
43286     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
43287
43288     // private
43289     onRender : function(ct, position){
43290         Roo.form.Column.superclass.onRender.call(this, ct, position);
43291         if(this.width){
43292             this.el.setWidth(this.width);
43293         }
43294     }
43295 });
43296
43297
43298 /**
43299  * @class Roo.form.Row
43300  * @extends Roo.form.Layout
43301  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
43302  * @constructor
43303  * @param {Object} config Configuration options
43304  */
43305
43306  
43307 Roo.form.Row = function(config){
43308     Roo.form.Row.superclass.constructor.call(this, config);
43309 };
43310  
43311 Roo.extend(Roo.form.Row, Roo.form.Layout, {
43312       /**
43313      * @cfg {Number/String} width
43314      * The fixed width of the column in pixels or CSS value (defaults to "auto")
43315      */
43316     /**
43317      * @cfg {Number/String} height
43318      * The fixed height of the column in pixels or CSS value (defaults to "auto")
43319      */
43320     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
43321     
43322     padWidth : 20,
43323     // private
43324     onRender : function(ct, position){
43325         //console.log('row render');
43326         if(!this.rowTpl){
43327             var t = new Roo.Template(
43328                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
43329                     '<label for="{0}" style="{2}">{1}{4}</label>',
43330                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
43331                     '</div>',
43332                 '</div>'
43333             );
43334             t.disableFormats = true;
43335             t.compile();
43336             Roo.form.Layout.prototype.rowTpl = t;
43337         }
43338         this.fieldTpl = this.rowTpl;
43339         
43340         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
43341         var labelWidth = 100;
43342         
43343         if ((this.labelAlign != 'top')) {
43344             if (typeof this.labelWidth == 'number') {
43345                 labelWidth = this.labelWidth
43346             }
43347             this.padWidth =  20 + labelWidth;
43348             
43349         }
43350         
43351         Roo.form.Column.superclass.onRender.call(this, ct, position);
43352         if(this.width){
43353             this.el.setWidth(this.width);
43354         }
43355         if(this.height){
43356             this.el.setHeight(this.height);
43357         }
43358     },
43359     
43360     // private
43361     renderField : function(f){
43362         f.fieldEl = this.fieldTpl.append(this.el, [
43363                f.id, f.fieldLabel,
43364                f.labelStyle||this.labelStyle||'',
43365                this.elementStyle||'',
43366                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
43367                f.itemCls||this.itemCls||'',
43368                f.width ? f.width + this.padWidth : 160 + this.padWidth
43369        ],true);
43370     }
43371 });
43372  
43373
43374 /**
43375  * @class Roo.form.FieldSet
43376  * @extends Roo.form.Layout
43377  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
43378  * @constructor
43379  * @param {Object} config Configuration options
43380  */
43381 Roo.form.FieldSet = function(config){
43382     Roo.form.FieldSet.superclass.constructor.call(this, config);
43383 };
43384
43385 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
43386     /**
43387      * @cfg {String} legend
43388      * The text to display as the legend for the FieldSet (defaults to '')
43389      */
43390     /**
43391      * @cfg {String/Object} autoCreate
43392      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
43393      */
43394
43395     // private
43396     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
43397
43398     // private
43399     onRender : function(ct, position){
43400         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
43401         if(this.legend){
43402             this.setLegend(this.legend);
43403         }
43404     },
43405
43406     // private
43407     setLegend : function(text){
43408         if(this.rendered){
43409             this.el.child('legend').update(text);
43410         }
43411     }
43412 });/*
43413  * Based on:
43414  * Ext JS Library 1.1.1
43415  * Copyright(c) 2006-2007, Ext JS, LLC.
43416  *
43417  * Originally Released Under LGPL - original licence link has changed is not relivant.
43418  *
43419  * Fork - LGPL
43420  * <script type="text/javascript">
43421  */
43422 /**
43423  * @class Roo.form.VTypes
43424  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
43425  * @singleton
43426  */
43427 Roo.form.VTypes = function(){
43428     // closure these in so they are only created once.
43429     var alpha = /^[a-zA-Z_]+$/;
43430     var alphanum = /^[a-zA-Z0-9_]+$/;
43431     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
43432     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
43433
43434     // All these messages and functions are configurable
43435     return {
43436         /**
43437          * The function used to validate email addresses
43438          * @param {String} value The email address
43439          */
43440         'email' : function(v){
43441             return email.test(v);
43442         },
43443         /**
43444          * The error text to display when the email validation function returns false
43445          * @type String
43446          */
43447         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
43448         /**
43449          * The keystroke filter mask to be applied on email input
43450          * @type RegExp
43451          */
43452         'emailMask' : /[a-z0-9_\.\-@]/i,
43453
43454         /**
43455          * The function used to validate URLs
43456          * @param {String} value The URL
43457          */
43458         'url' : function(v){
43459             return url.test(v);
43460         },
43461         /**
43462          * The error text to display when the url validation function returns false
43463          * @type String
43464          */
43465         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
43466         
43467         /**
43468          * The function used to validate alpha values
43469          * @param {String} value The value
43470          */
43471         'alpha' : function(v){
43472             return alpha.test(v);
43473         },
43474         /**
43475          * The error text to display when the alpha validation function returns false
43476          * @type String
43477          */
43478         'alphaText' : 'This field should only contain letters and _',
43479         /**
43480          * The keystroke filter mask to be applied on alpha input
43481          * @type RegExp
43482          */
43483         'alphaMask' : /[a-z_]/i,
43484
43485         /**
43486          * The function used to validate alphanumeric values
43487          * @param {String} value The value
43488          */
43489         'alphanum' : function(v){
43490             return alphanum.test(v);
43491         },
43492         /**
43493          * The error text to display when the alphanumeric validation function returns false
43494          * @type String
43495          */
43496         'alphanumText' : 'This field should only contain letters, numbers and _',
43497         /**
43498          * The keystroke filter mask to be applied on alphanumeric input
43499          * @type RegExp
43500          */
43501         'alphanumMask' : /[a-z0-9_]/i
43502     };
43503 }();//<script type="text/javascript">
43504
43505 /**
43506  * @class Roo.form.FCKeditor
43507  * @extends Roo.form.TextArea
43508  * Wrapper around the FCKEditor http://www.fckeditor.net
43509  * @constructor
43510  * Creates a new FCKeditor
43511  * @param {Object} config Configuration options
43512  */
43513 Roo.form.FCKeditor = function(config){
43514     Roo.form.FCKeditor.superclass.constructor.call(this, config);
43515     this.addEvents({
43516          /**
43517          * @event editorinit
43518          * Fired when the editor is initialized - you can add extra handlers here..
43519          * @param {FCKeditor} this
43520          * @param {Object} the FCK object.
43521          */
43522         editorinit : true
43523     });
43524     
43525     
43526 };
43527 Roo.form.FCKeditor.editors = { };
43528 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
43529 {
43530     //defaultAutoCreate : {
43531     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
43532     //},
43533     // private
43534     /**
43535      * @cfg {Object} fck options - see fck manual for details.
43536      */
43537     fckconfig : false,
43538     
43539     /**
43540      * @cfg {Object} fck toolbar set (Basic or Default)
43541      */
43542     toolbarSet : 'Basic',
43543     /**
43544      * @cfg {Object} fck BasePath
43545      */ 
43546     basePath : '/fckeditor/',
43547     
43548     
43549     frame : false,
43550     
43551     value : '',
43552     
43553    
43554     onRender : function(ct, position)
43555     {
43556         if(!this.el){
43557             this.defaultAutoCreate = {
43558                 tag: "textarea",
43559                 style:"width:300px;height:60px;",
43560                 autocomplete: "off"
43561             };
43562         }
43563         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
43564         /*
43565         if(this.grow){
43566             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
43567             if(this.preventScrollbars){
43568                 this.el.setStyle("overflow", "hidden");
43569             }
43570             this.el.setHeight(this.growMin);
43571         }
43572         */
43573         //console.log('onrender' + this.getId() );
43574         Roo.form.FCKeditor.editors[this.getId()] = this;
43575          
43576
43577         this.replaceTextarea() ;
43578         
43579     },
43580     
43581     getEditor : function() {
43582         return this.fckEditor;
43583     },
43584     /**
43585      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43586      * @param {Mixed} value The value to set
43587      */
43588     
43589     
43590     setValue : function(value)
43591     {
43592         //console.log('setValue: ' + value);
43593         
43594         if(typeof(value) == 'undefined') { // not sure why this is happending...
43595             return;
43596         }
43597         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43598         
43599         //if(!this.el || !this.getEditor()) {
43600         //    this.value = value;
43601             //this.setValue.defer(100,this,[value]);    
43602         //    return;
43603         //} 
43604         
43605         if(!this.getEditor()) {
43606             return;
43607         }
43608         
43609         this.getEditor().SetData(value);
43610         
43611         //
43612
43613     },
43614
43615     /**
43616      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43617      * @return {Mixed} value The field value
43618      */
43619     getValue : function()
43620     {
43621         
43622         if (this.frame && this.frame.dom.style.display == 'none') {
43623             return Roo.form.FCKeditor.superclass.getValue.call(this);
43624         }
43625         
43626         if(!this.el || !this.getEditor()) {
43627            
43628            // this.getValue.defer(100,this); 
43629             return this.value;
43630         }
43631        
43632         
43633         var value=this.getEditor().GetData();
43634         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43635         return Roo.form.FCKeditor.superclass.getValue.call(this);
43636         
43637
43638     },
43639
43640     /**
43641      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43642      * @return {Mixed} value The field value
43643      */
43644     getRawValue : function()
43645     {
43646         if (this.frame && this.frame.dom.style.display == 'none') {
43647             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43648         }
43649         
43650         if(!this.el || !this.getEditor()) {
43651             //this.getRawValue.defer(100,this); 
43652             return this.value;
43653             return;
43654         }
43655         
43656         
43657         
43658         var value=this.getEditor().GetData();
43659         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43660         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43661          
43662     },
43663     
43664     setSize : function(w,h) {
43665         
43666         
43667         
43668         //if (this.frame && this.frame.dom.style.display == 'none') {
43669         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43670         //    return;
43671         //}
43672         //if(!this.el || !this.getEditor()) {
43673         //    this.setSize.defer(100,this, [w,h]); 
43674         //    return;
43675         //}
43676         
43677         
43678         
43679         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43680         
43681         this.frame.dom.setAttribute('width', w);
43682         this.frame.dom.setAttribute('height', h);
43683         this.frame.setSize(w,h);
43684         
43685     },
43686     
43687     toggleSourceEdit : function(value) {
43688         
43689       
43690          
43691         this.el.dom.style.display = value ? '' : 'none';
43692         this.frame.dom.style.display = value ?  'none' : '';
43693         
43694     },
43695     
43696     
43697     focus: function(tag)
43698     {
43699         if (this.frame.dom.style.display == 'none') {
43700             return Roo.form.FCKeditor.superclass.focus.call(this);
43701         }
43702         if(!this.el || !this.getEditor()) {
43703             this.focus.defer(100,this, [tag]); 
43704             return;
43705         }
43706         
43707         
43708         
43709         
43710         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43711         this.getEditor().Focus();
43712         if (tgs.length) {
43713             if (!this.getEditor().Selection.GetSelection()) {
43714                 this.focus.defer(100,this, [tag]); 
43715                 return;
43716             }
43717             
43718             
43719             var r = this.getEditor().EditorDocument.createRange();
43720             r.setStart(tgs[0],0);
43721             r.setEnd(tgs[0],0);
43722             this.getEditor().Selection.GetSelection().removeAllRanges();
43723             this.getEditor().Selection.GetSelection().addRange(r);
43724             this.getEditor().Focus();
43725         }
43726         
43727     },
43728     
43729     
43730     
43731     replaceTextarea : function()
43732     {
43733         if ( document.getElementById( this.getId() + '___Frame' ) )
43734             return ;
43735         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43736         //{
43737             // We must check the elements firstly using the Id and then the name.
43738         var oTextarea = document.getElementById( this.getId() );
43739         
43740         var colElementsByName = document.getElementsByName( this.getId() ) ;
43741          
43742         oTextarea.style.display = 'none' ;
43743
43744         if ( oTextarea.tabIndex ) {            
43745             this.TabIndex = oTextarea.tabIndex ;
43746         }
43747         
43748         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43749         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43750         this.frame = Roo.get(this.getId() + '___Frame')
43751     },
43752     
43753     _getConfigHtml : function()
43754     {
43755         var sConfig = '' ;
43756
43757         for ( var o in this.fckconfig ) {
43758             sConfig += sConfig.length > 0  ? '&amp;' : '';
43759             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43760         }
43761
43762         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43763     },
43764     
43765     
43766     _getIFrameHtml : function()
43767     {
43768         var sFile = 'fckeditor.html' ;
43769         /* no idea what this is about..
43770         try
43771         {
43772             if ( (/fcksource=true/i).test( window.top.location.search ) )
43773                 sFile = 'fckeditor.original.html' ;
43774         }
43775         catch (e) { 
43776         */
43777
43778         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43779         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43780         
43781         
43782         var html = '<iframe id="' + this.getId() +
43783             '___Frame" src="' + sLink +
43784             '" width="' + this.width +
43785             '" height="' + this.height + '"' +
43786             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43787             ' frameborder="0" scrolling="no"></iframe>' ;
43788
43789         return html ;
43790     },
43791     
43792     _insertHtmlBefore : function( html, element )
43793     {
43794         if ( element.insertAdjacentHTML )       {
43795             // IE
43796             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43797         } else { // Gecko
43798             var oRange = document.createRange() ;
43799             oRange.setStartBefore( element ) ;
43800             var oFragment = oRange.createContextualFragment( html );
43801             element.parentNode.insertBefore( oFragment, element ) ;
43802         }
43803     }
43804     
43805     
43806   
43807     
43808     
43809     
43810     
43811
43812 });
43813
43814 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43815
43816 function FCKeditor_OnComplete(editorInstance){
43817     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43818     f.fckEditor = editorInstance;
43819     //console.log("loaded");
43820     f.fireEvent('editorinit', f, editorInstance);
43821
43822   
43823
43824  
43825
43826
43827
43828
43829
43830
43831
43832
43833
43834
43835
43836
43837
43838
43839
43840 //<script type="text/javascript">
43841 /**
43842  * @class Roo.form.GridField
43843  * @extends Roo.form.Field
43844  * Embed a grid (or editable grid into a form)
43845  * STATUS ALPHA
43846  * 
43847  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43848  * it needs 
43849  * xgrid.store = Roo.data.Store
43850  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43851  * xgrid.store.reader = Roo.data.JsonReader 
43852  * 
43853  * 
43854  * @constructor
43855  * Creates a new GridField
43856  * @param {Object} config Configuration options
43857  */
43858 Roo.form.GridField = function(config){
43859     Roo.form.GridField.superclass.constructor.call(this, config);
43860      
43861 };
43862
43863 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43864     /**
43865      * @cfg {Number} width  - used to restrict width of grid..
43866      */
43867     width : 100,
43868     /**
43869      * @cfg {Number} height - used to restrict height of grid..
43870      */
43871     height : 50,
43872      /**
43873      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43874          * 
43875          *}
43876      */
43877     xgrid : false, 
43878     /**
43879      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43880      * {tag: "input", type: "checkbox", autocomplete: "off"})
43881      */
43882    // defaultAutoCreate : { tag: 'div' },
43883     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43884     /**
43885      * @cfg {String} addTitle Text to include for adding a title.
43886      */
43887     addTitle : false,
43888     //
43889     onResize : function(){
43890         Roo.form.Field.superclass.onResize.apply(this, arguments);
43891     },
43892
43893     initEvents : function(){
43894         // Roo.form.Checkbox.superclass.initEvents.call(this);
43895         // has no events...
43896        
43897     },
43898
43899
43900     getResizeEl : function(){
43901         return this.wrap;
43902     },
43903
43904     getPositionEl : function(){
43905         return this.wrap;
43906     },
43907
43908     // private
43909     onRender : function(ct, position){
43910         
43911         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43912         var style = this.style;
43913         delete this.style;
43914         
43915         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43916         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43917         this.viewEl = this.wrap.createChild({ tag: 'div' });
43918         if (style) {
43919             this.viewEl.applyStyles(style);
43920         }
43921         if (this.width) {
43922             this.viewEl.setWidth(this.width);
43923         }
43924         if (this.height) {
43925             this.viewEl.setHeight(this.height);
43926         }
43927         //if(this.inputValue !== undefined){
43928         //this.setValue(this.value);
43929         
43930         
43931         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43932         
43933         
43934         this.grid.render();
43935         this.grid.getDataSource().on('remove', this.refreshValue, this);
43936         this.grid.getDataSource().on('update', this.refreshValue, this);
43937         this.grid.on('afteredit', this.refreshValue, this);
43938  
43939     },
43940      
43941     
43942     /**
43943      * Sets the value of the item. 
43944      * @param {String} either an object  or a string..
43945      */
43946     setValue : function(v){
43947         //this.value = v;
43948         v = v || []; // empty set..
43949         // this does not seem smart - it really only affects memoryproxy grids..
43950         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43951             var ds = this.grid.getDataSource();
43952             // assumes a json reader..
43953             var data = {}
43954             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43955             ds.loadData( data);
43956         }
43957         // clear selection so it does not get stale.
43958         if (this.grid.sm) { 
43959             this.grid.sm.clearSelections();
43960         }
43961         
43962         Roo.form.GridField.superclass.setValue.call(this, v);
43963         this.refreshValue();
43964         // should load data in the grid really....
43965     },
43966     
43967     // private
43968     refreshValue: function() {
43969          var val = [];
43970         this.grid.getDataSource().each(function(r) {
43971             val.push(r.data);
43972         });
43973         this.el.dom.value = Roo.encode(val);
43974     }
43975     
43976      
43977     
43978     
43979 });/*
43980  * Based on:
43981  * Ext JS Library 1.1.1
43982  * Copyright(c) 2006-2007, Ext JS, LLC.
43983  *
43984  * Originally Released Under LGPL - original licence link has changed is not relivant.
43985  *
43986  * Fork - LGPL
43987  * <script type="text/javascript">
43988  */
43989 /**
43990  * @class Roo.form.DisplayField
43991  * @extends Roo.form.Field
43992  * A generic Field to display non-editable data.
43993  * @constructor
43994  * Creates a new Display Field item.
43995  * @param {Object} config Configuration options
43996  */
43997 Roo.form.DisplayField = function(config){
43998     Roo.form.DisplayField.superclass.constructor.call(this, config);
43999     
44000 };
44001
44002 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
44003     inputType:      'hidden',
44004     allowBlank:     true,
44005     readOnly:         true,
44006     
44007  
44008     /**
44009      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44010      */
44011     focusClass : undefined,
44012     /**
44013      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44014      */
44015     fieldClass: 'x-form-field',
44016     
44017      /**
44018      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
44019      */
44020     valueRenderer: undefined,
44021     
44022     width: 100,
44023     /**
44024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44025      * {tag: "input", type: "checkbox", autocomplete: "off"})
44026      */
44027      
44028  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
44029
44030     onResize : function(){
44031         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
44032         
44033     },
44034
44035     initEvents : function(){
44036         // Roo.form.Checkbox.superclass.initEvents.call(this);
44037         // has no events...
44038        
44039     },
44040
44041
44042     getResizeEl : function(){
44043         return this.wrap;
44044     },
44045
44046     getPositionEl : function(){
44047         return this.wrap;
44048     },
44049
44050     // private
44051     onRender : function(ct, position){
44052         
44053         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
44054         //if(this.inputValue !== undefined){
44055         this.wrap = this.el.wrap();
44056         
44057         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
44058         
44059         if (this.bodyStyle) {
44060             this.viewEl.applyStyles(this.bodyStyle);
44061         }
44062         //this.viewEl.setStyle('padding', '2px');
44063         
44064         this.setValue(this.value);
44065         
44066     },
44067 /*
44068     // private
44069     initValue : Roo.emptyFn,
44070
44071   */
44072
44073         // private
44074     onClick : function(){
44075         
44076     },
44077
44078     /**
44079      * Sets the checked state of the checkbox.
44080      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
44081      */
44082     setValue : function(v){
44083         this.value = v;
44084         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
44085         // this might be called before we have a dom element..
44086         if (!this.viewEl) {
44087             return;
44088         }
44089         this.viewEl.dom.innerHTML = html;
44090         Roo.form.DisplayField.superclass.setValue.call(this, v);
44091
44092     }
44093 });/*
44094  * 
44095  * Licence- LGPL
44096  * 
44097  */
44098
44099 /**
44100  * @class Roo.form.DayPicker
44101  * @extends Roo.form.Field
44102  * A Day picker show [M] [T] [W] ....
44103  * @constructor
44104  * Creates a new Day Picker
44105  * @param {Object} config Configuration options
44106  */
44107 Roo.form.DayPicker= function(config){
44108     Roo.form.DayPicker.superclass.constructor.call(this, config);
44109      
44110 };
44111
44112 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
44113     /**
44114      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44115      */
44116     focusClass : undefined,
44117     /**
44118      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44119      */
44120     fieldClass: "x-form-field",
44121    
44122     /**
44123      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44124      * {tag: "input", type: "checkbox", autocomplete: "off"})
44125      */
44126     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44127     
44128    
44129     actionMode : 'viewEl', 
44130     //
44131     // private
44132  
44133     inputType : 'hidden',
44134     
44135      
44136     inputElement: false, // real input element?
44137     basedOn: false, // ????
44138     
44139     isFormField: true, // not sure where this is needed!!!!
44140
44141     onResize : function(){
44142         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44143         if(!this.boxLabel){
44144             this.el.alignTo(this.wrap, 'c-c');
44145         }
44146     },
44147
44148     initEvents : function(){
44149         Roo.form.Checkbox.superclass.initEvents.call(this);
44150         this.el.on("click", this.onClick,  this);
44151         this.el.on("change", this.onClick,  this);
44152     },
44153
44154
44155     getResizeEl : function(){
44156         return this.wrap;
44157     },
44158
44159     getPositionEl : function(){
44160         return this.wrap;
44161     },
44162
44163     
44164     // private
44165     onRender : function(ct, position){
44166         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44167        
44168         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
44169         
44170         var r1 = '<table><tr>';
44171         var r2 = '<tr class="x-form-daypick-icons">';
44172         for (var i=0; i < 7; i++) {
44173             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
44174             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
44175         }
44176         
44177         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
44178         viewEl.select('img').on('click', this.onClick, this);
44179         this.viewEl = viewEl;   
44180         
44181         
44182         // this will not work on Chrome!!!
44183         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44184         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44185         
44186         
44187           
44188
44189     },
44190
44191     // private
44192     initValue : Roo.emptyFn,
44193
44194     /**
44195      * Returns the checked state of the checkbox.
44196      * @return {Boolean} True if checked, else false
44197      */
44198     getValue : function(){
44199         return this.el.dom.value;
44200         
44201     },
44202
44203         // private
44204     onClick : function(e){ 
44205         //this.setChecked(!this.checked);
44206         Roo.get(e.target).toggleClass('x-menu-item-checked');
44207         this.refreshValue();
44208         //if(this.el.dom.checked != this.checked){
44209         //    this.setValue(this.el.dom.checked);
44210        // }
44211     },
44212     
44213     // private
44214     refreshValue : function()
44215     {
44216         var val = '';
44217         this.viewEl.select('img',true).each(function(e,i,n)  {
44218             val += e.is(".x-menu-item-checked") ? String(n) : '';
44219         });
44220         this.setValue(val, true);
44221     },
44222
44223     /**
44224      * Sets the checked state of the checkbox.
44225      * On is always based on a string comparison between inputValue and the param.
44226      * @param {Boolean/String} value - the value to set 
44227      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44228      */
44229     setValue : function(v,suppressEvent){
44230         if (!this.el.dom) {
44231             return;
44232         }
44233         var old = this.el.dom.value ;
44234         this.el.dom.value = v;
44235         if (suppressEvent) {
44236             return ;
44237         }
44238          
44239         // update display..
44240         this.viewEl.select('img',true).each(function(e,i,n)  {
44241             
44242             var on = e.is(".x-menu-item-checked");
44243             var newv = v.indexOf(String(n)) > -1;
44244             if (on != newv) {
44245                 e.toggleClass('x-menu-item-checked');
44246             }
44247             
44248         });
44249         
44250         
44251         this.fireEvent('change', this, v, old);
44252         
44253         
44254     },
44255    
44256     // handle setting of hidden value by some other method!!?!?
44257     setFromHidden: function()
44258     {
44259         if(!this.el){
44260             return;
44261         }
44262         //console.log("SET FROM HIDDEN");
44263         //alert('setFrom hidden');
44264         this.setValue(this.el.dom.value);
44265     },
44266     
44267     onDestroy : function()
44268     {
44269         if(this.viewEl){
44270             Roo.get(this.viewEl).remove();
44271         }
44272          
44273         Roo.form.DayPicker.superclass.onDestroy.call(this);
44274     }
44275
44276 });/*
44277  * RooJS Library 1.1.1
44278  * Copyright(c) 2008-2011  Alan Knowles
44279  *
44280  * License - LGPL
44281  */
44282  
44283
44284 /**
44285  * @class Roo.form.ComboCheck
44286  * @extends Roo.form.ComboBox
44287  * A combobox for multiple select items.
44288  *
44289  * FIXME - could do with a reset button..
44290  * 
44291  * @constructor
44292  * Create a new ComboCheck
44293  * @param {Object} config Configuration options
44294  */
44295 Roo.form.ComboCheck = function(config){
44296     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44297     // should verify some data...
44298     // like
44299     // hiddenName = required..
44300     // displayField = required
44301     // valudField == required
44302     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44303     var _t = this;
44304     Roo.each(req, function(e) {
44305         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44306             throw "Roo.form.ComboCheck : missing value for: " + e;
44307         }
44308     });
44309     
44310     
44311 };
44312
44313 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
44314      
44315      
44316     editable : false,
44317      
44318     selectedClass: 'x-menu-item-checked', 
44319     
44320     // private
44321     onRender : function(ct, position){
44322         var _t = this;
44323         
44324         
44325         
44326         if(!this.tpl){
44327             var cls = 'x-combo-list';
44328
44329             
44330             this.tpl =  new Roo.Template({
44331                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
44332                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
44333                    '<span>{' + this.displayField + '}</span>' +
44334                     '</div>' 
44335                 
44336             });
44337         }
44338  
44339         
44340         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
44341         this.view.singleSelect = false;
44342         this.view.multiSelect = true;
44343         this.view.toggleSelect = true;
44344         this.pageTb.add(new Roo.Toolbar.Fill(), {
44345             
44346             text: 'Done',
44347             handler: function()
44348             {
44349                 _t.collapse();
44350             }
44351         });
44352     },
44353     
44354     onViewOver : function(e, t){
44355         // do nothing...
44356         return;
44357         
44358     },
44359     
44360     onViewClick : function(doFocus,index){
44361         return;
44362         
44363     },
44364     select: function () {
44365         //Roo.log("SELECT CALLED");
44366     },
44367      
44368     selectByValue : function(xv, scrollIntoView){
44369         var ar = this.getValueArray();
44370         var sels = [];
44371         
44372         Roo.each(ar, function(v) {
44373             if(v === undefined || v === null){
44374                 return;
44375             }
44376             var r = this.findRecord(this.valueField, v);
44377             if(r){
44378                 sels.push(this.store.indexOf(r))
44379                 
44380             }
44381         },this);
44382         this.view.select(sels);
44383         return false;
44384     },
44385     
44386     
44387     
44388     onSelect : function(record, index){
44389        // Roo.log("onselect Called");
44390        // this is only called by the clear button now..
44391         this.view.clearSelections();
44392         this.setValue('[]');
44393         if (this.value != this.valueBefore) {
44394             this.fireEvent('change', this, this.value, this.valueBefore);
44395         }
44396     },
44397     getValueArray : function()
44398     {
44399         var ar = [] ;
44400         
44401         try {
44402             //Roo.log(this.value);
44403             if (typeof(this.value) == 'undefined') {
44404                 return [];
44405             }
44406             var ar = Roo.decode(this.value);
44407             return  ar instanceof Array ? ar : []; //?? valid?
44408             
44409         } catch(e) {
44410             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
44411             return [];
44412         }
44413          
44414     },
44415     expand : function ()
44416     {
44417         Roo.form.ComboCheck.superclass.expand.call(this);
44418         this.valueBefore = this.value;
44419         
44420
44421     },
44422     
44423     collapse : function(){
44424         Roo.form.ComboCheck.superclass.collapse.call(this);
44425         var sl = this.view.getSelectedIndexes();
44426         var st = this.store;
44427         var nv = [];
44428         var tv = [];
44429         var r;
44430         Roo.each(sl, function(i) {
44431             r = st.getAt(i);
44432             nv.push(r.get(this.valueField));
44433         },this);
44434         this.setValue(Roo.encode(nv));
44435         if (this.value != this.valueBefore) {
44436
44437             this.fireEvent('change', this, this.value, this.valueBefore);
44438         }
44439         
44440     },
44441     
44442     setValue : function(v){
44443         // Roo.log(v);
44444         this.value = v;
44445         
44446         var vals = this.getValueArray();
44447         var tv = [];
44448         Roo.each(vals, function(k) {
44449             var r = this.findRecord(this.valueField, k);
44450             if(r){
44451                 tv.push(r.data[this.displayField]);
44452             }else if(this.valueNotFoundText !== undefined){
44453                 tv.push( this.valueNotFoundText );
44454             }
44455         },this);
44456        // Roo.log(tv);
44457         
44458         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
44459         this.hiddenField.value = v;
44460         this.value = v;
44461     }
44462     
44463 });//<script type="text/javasscript">
44464  
44465
44466 /**
44467  * @class Roo.DDView
44468  * A DnD enabled version of Roo.View.
44469  * @param {Element/String} container The Element in which to create the View.
44470  * @param {String} tpl The template string used to create the markup for each element of the View
44471  * @param {Object} config The configuration properties. These include all the config options of
44472  * {@link Roo.View} plus some specific to this class.<br>
44473  * <p>
44474  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
44475  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
44476  * <p>
44477  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
44478 .x-view-drag-insert-above {
44479         border-top:1px dotted #3366cc;
44480 }
44481 .x-view-drag-insert-below {
44482         border-bottom:1px dotted #3366cc;
44483 }
44484 </code></pre>
44485  * 
44486  */
44487  
44488 Roo.DDView = function(container, tpl, config) {
44489     Roo.DDView.superclass.constructor.apply(this, arguments);
44490     this.getEl().setStyle("outline", "0px none");
44491     this.getEl().unselectable();
44492     if (this.dragGroup) {
44493                 this.setDraggable(this.dragGroup.split(","));
44494     }
44495     if (this.dropGroup) {
44496                 this.setDroppable(this.dropGroup.split(","));
44497     }
44498     if (this.deletable) {
44499         this.setDeletable();
44500     }
44501     this.isDirtyFlag = false;
44502         this.addEvents({
44503                 "drop" : true
44504         });
44505 };
44506
44507 Roo.extend(Roo.DDView, Roo.View, {
44508 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
44509 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
44510 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
44511 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
44512
44513         isFormField: true,
44514
44515         reset: Roo.emptyFn,
44516         
44517         clearInvalid: Roo.form.Field.prototype.clearInvalid,
44518
44519         validate: function() {
44520                 return true;
44521         },
44522         
44523         destroy: function() {
44524                 this.purgeListeners();
44525                 this.getEl.removeAllListeners();
44526                 this.getEl().remove();
44527                 if (this.dragZone) {
44528                         if (this.dragZone.destroy) {
44529                                 this.dragZone.destroy();
44530                         }
44531                 }
44532                 if (this.dropZone) {
44533                         if (this.dropZone.destroy) {
44534                                 this.dropZone.destroy();
44535                         }
44536                 }
44537         },
44538
44539 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
44540         getName: function() {
44541                 return this.name;
44542         },
44543
44544 /**     Loads the View from a JSON string representing the Records to put into the Store. */
44545         setValue: function(v) {
44546                 if (!this.store) {
44547                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
44548                 }
44549                 var data = {};
44550                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
44551                 this.store.proxy = new Roo.data.MemoryProxy(data);
44552                 this.store.load();
44553         },
44554
44555 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
44556         getValue: function() {
44557                 var result = '(';
44558                 this.store.each(function(rec) {
44559                         result += rec.id + ',';
44560                 });
44561                 return result.substr(0, result.length - 1) + ')';
44562         },
44563         
44564         getIds: function() {
44565                 var i = 0, result = new Array(this.store.getCount());
44566                 this.store.each(function(rec) {
44567                         result[i++] = rec.id;
44568                 });
44569                 return result;
44570         },
44571         
44572         isDirty: function() {
44573                 return this.isDirtyFlag;
44574         },
44575
44576 /**
44577  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
44578  *      whole Element becomes the target, and this causes the drop gesture to append.
44579  */
44580     getTargetFromEvent : function(e) {
44581                 var target = e.getTarget();
44582                 while ((target !== null) && (target.parentNode != this.el.dom)) {
44583                 target = target.parentNode;
44584                 }
44585                 if (!target) {
44586                         target = this.el.dom.lastChild || this.el.dom;
44587                 }
44588                 return target;
44589     },
44590
44591 /**
44592  *      Create the drag data which consists of an object which has the property "ddel" as
44593  *      the drag proxy element. 
44594  */
44595     getDragData : function(e) {
44596         var target = this.findItemFromChild(e.getTarget());
44597                 if(target) {
44598                         this.handleSelection(e);
44599                         var selNodes = this.getSelectedNodes();
44600             var dragData = {
44601                 source: this,
44602                 copy: this.copy || (this.allowCopy && e.ctrlKey),
44603                 nodes: selNodes,
44604                 records: []
44605                         };
44606                         var selectedIndices = this.getSelectedIndexes();
44607                         for (var i = 0; i < selectedIndices.length; i++) {
44608                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
44609                         }
44610                         if (selNodes.length == 1) {
44611                                 dragData.ddel = target.cloneNode(true); // the div element
44612                         } else {
44613                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44614                                 div.className = 'multi-proxy';
44615                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44616                                         div.appendChild(selNodes[i].cloneNode(true));
44617                                 }
44618                                 dragData.ddel = div;
44619                         }
44620             //console.log(dragData)
44621             //console.log(dragData.ddel.innerHTML)
44622                         return dragData;
44623                 }
44624         //console.log('nodragData')
44625                 return false;
44626     },
44627     
44628 /**     Specify to which ddGroup items in this DDView may be dragged. */
44629     setDraggable: function(ddGroup) {
44630         if (ddGroup instanceof Array) {
44631                 Roo.each(ddGroup, this.setDraggable, this);
44632                 return;
44633         }
44634         if (this.dragZone) {
44635                 this.dragZone.addToGroup(ddGroup);
44636         } else {
44637                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44638                                 containerScroll: true,
44639                                 ddGroup: ddGroup 
44640
44641                         });
44642 //                      Draggability implies selection. DragZone's mousedown selects the element.
44643                         if (!this.multiSelect) { this.singleSelect = true; }
44644
44645 //                      Wire the DragZone's handlers up to methods in *this*
44646                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44647                 }
44648     },
44649
44650 /**     Specify from which ddGroup this DDView accepts drops. */
44651     setDroppable: function(ddGroup) {
44652         if (ddGroup instanceof Array) {
44653                 Roo.each(ddGroup, this.setDroppable, this);
44654                 return;
44655         }
44656         if (this.dropZone) {
44657                 this.dropZone.addToGroup(ddGroup);
44658         } else {
44659                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44660                                 containerScroll: true,
44661                                 ddGroup: ddGroup
44662                         });
44663
44664 //                      Wire the DropZone's handlers up to methods in *this*
44665                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44666                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44667                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44668                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44669                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44670                 }
44671     },
44672
44673 /**     Decide whether to drop above or below a View node. */
44674     getDropPoint : function(e, n, dd){
44675         if (n == this.el.dom) { return "above"; }
44676                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44677                 var c = t + (b - t) / 2;
44678                 var y = Roo.lib.Event.getPageY(e);
44679                 if(y <= c) {
44680                         return "above";
44681                 }else{
44682                         return "below";
44683                 }
44684     },
44685
44686     onNodeEnter : function(n, dd, e, data){
44687                 return false;
44688     },
44689     
44690     onNodeOver : function(n, dd, e, data){
44691                 var pt = this.getDropPoint(e, n, dd);
44692                 // set the insert point style on the target node
44693                 var dragElClass = this.dropNotAllowed;
44694                 if (pt) {
44695                         var targetElClass;
44696                         if (pt == "above"){
44697                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44698                                 targetElClass = "x-view-drag-insert-above";
44699                         } else {
44700                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44701                                 targetElClass = "x-view-drag-insert-below";
44702                         }
44703                         if (this.lastInsertClass != targetElClass){
44704                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44705                                 this.lastInsertClass = targetElClass;
44706                         }
44707                 }
44708                 return dragElClass;
44709         },
44710
44711     onNodeOut : function(n, dd, e, data){
44712                 this.removeDropIndicators(n);
44713     },
44714
44715     onNodeDrop : function(n, dd, e, data){
44716         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44717                 return false;
44718         }
44719         var pt = this.getDropPoint(e, n, dd);
44720                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44721                 if (pt == "below") { insertAt++; }
44722                 for (var i = 0; i < data.records.length; i++) {
44723                         var r = data.records[i];
44724                         var dup = this.store.getById(r.id);
44725                         if (dup && (dd != this.dragZone)) {
44726                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44727                         } else {
44728                                 if (data.copy) {
44729                                         this.store.insert(insertAt++, r.copy());
44730                                 } else {
44731                                         data.source.isDirtyFlag = true;
44732                                         r.store.remove(r);
44733                                         this.store.insert(insertAt++, r);
44734                                 }
44735                                 this.isDirtyFlag = true;
44736                         }
44737                 }
44738                 this.dragZone.cachedTarget = null;
44739                 return true;
44740     },
44741
44742     removeDropIndicators : function(n){
44743                 if(n){
44744                         Roo.fly(n).removeClass([
44745                                 "x-view-drag-insert-above",
44746                                 "x-view-drag-insert-below"]);
44747                         this.lastInsertClass = "_noclass";
44748                 }
44749     },
44750
44751 /**
44752  *      Utility method. Add a delete option to the DDView's context menu.
44753  *      @param {String} imageUrl The URL of the "delete" icon image.
44754  */
44755         setDeletable: function(imageUrl) {
44756                 if (!this.singleSelect && !this.multiSelect) {
44757                         this.singleSelect = true;
44758                 }
44759                 var c = this.getContextMenu();
44760                 this.contextMenu.on("itemclick", function(item) {
44761                         switch (item.id) {
44762                                 case "delete":
44763                                         this.remove(this.getSelectedIndexes());
44764                                         break;
44765                         }
44766                 }, this);
44767                 this.contextMenu.add({
44768                         icon: imageUrl,
44769                         id: "delete",
44770                         text: 'Delete'
44771                 });
44772         },
44773         
44774 /**     Return the context menu for this DDView. */
44775         getContextMenu: function() {
44776                 if (!this.contextMenu) {
44777 //                      Create the View's context menu
44778                         this.contextMenu = new Roo.menu.Menu({
44779                                 id: this.id + "-contextmenu"
44780                         });
44781                         this.el.on("contextmenu", this.showContextMenu, this);
44782                 }
44783                 return this.contextMenu;
44784         },
44785         
44786         disableContextMenu: function() {
44787                 if (this.contextMenu) {
44788                         this.el.un("contextmenu", this.showContextMenu, this);
44789                 }
44790         },
44791
44792         showContextMenu: function(e, item) {
44793         item = this.findItemFromChild(e.getTarget());
44794                 if (item) {
44795                         e.stopEvent();
44796                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44797                         this.contextMenu.showAt(e.getXY());
44798             }
44799     },
44800
44801 /**
44802  *      Remove {@link Roo.data.Record}s at the specified indices.
44803  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44804  */
44805     remove: function(selectedIndices) {
44806                 selectedIndices = [].concat(selectedIndices);
44807                 for (var i = 0; i < selectedIndices.length; i++) {
44808                         var rec = this.store.getAt(selectedIndices[i]);
44809                         this.store.remove(rec);
44810                 }
44811     },
44812
44813 /**
44814  *      Double click fires the event, but also, if this is draggable, and there is only one other
44815  *      related DropZone, it transfers the selected node.
44816  */
44817     onDblClick : function(e){
44818         var item = this.findItemFromChild(e.getTarget());
44819         if(item){
44820             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44821                 return false;
44822             }
44823             if (this.dragGroup) {
44824                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44825                     while (targets.indexOf(this.dropZone) > -1) {
44826                             targets.remove(this.dropZone);
44827                                 }
44828                     if (targets.length == 1) {
44829                                         this.dragZone.cachedTarget = null;
44830                         var el = Roo.get(targets[0].getEl());
44831                         var box = el.getBox(true);
44832                         targets[0].onNodeDrop(el.dom, {
44833                                 target: el.dom,
44834                                 xy: [box.x, box.y + box.height - 1]
44835                         }, null, this.getDragData(e));
44836                     }
44837                 }
44838         }
44839     },
44840     
44841     handleSelection: function(e) {
44842                 this.dragZone.cachedTarget = null;
44843         var item = this.findItemFromChild(e.getTarget());
44844         if (!item) {
44845                 this.clearSelections(true);
44846                 return;
44847         }
44848                 if (item && (this.multiSelect || this.singleSelect)){
44849                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44850                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44851                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44852                                 this.unselect(item);
44853                         } else {
44854                                 this.select(item, this.multiSelect && e.ctrlKey);
44855                                 this.lastSelection = item;
44856                         }
44857                 }
44858     },
44859
44860     onItemClick : function(item, index, e){
44861                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44862                         return false;
44863                 }
44864                 return true;
44865     },
44866
44867     unselect : function(nodeInfo, suppressEvent){
44868                 var node = this.getNode(nodeInfo);
44869                 if(node && this.isSelected(node)){
44870                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44871                                 Roo.fly(node).removeClass(this.selectedClass);
44872                                 this.selections.remove(node);
44873                                 if(!suppressEvent){
44874                                         this.fireEvent("selectionchange", this, this.selections);
44875                                 }
44876                         }
44877                 }
44878     }
44879 });
44880 /*
44881  * Based on:
44882  * Ext JS Library 1.1.1
44883  * Copyright(c) 2006-2007, Ext JS, LLC.
44884  *
44885  * Originally Released Under LGPL - original licence link has changed is not relivant.
44886  *
44887  * Fork - LGPL
44888  * <script type="text/javascript">
44889  */
44890  
44891 /**
44892  * @class Roo.LayoutManager
44893  * @extends Roo.util.Observable
44894  * Base class for layout managers.
44895  */
44896 Roo.LayoutManager = function(container, config){
44897     Roo.LayoutManager.superclass.constructor.call(this);
44898     this.el = Roo.get(container);
44899     // ie scrollbar fix
44900     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44901         document.body.scroll = "no";
44902     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44903         this.el.position('relative');
44904     }
44905     this.id = this.el.id;
44906     this.el.addClass("x-layout-container");
44907     /** false to disable window resize monitoring @type Boolean */
44908     this.monitorWindowResize = true;
44909     this.regions = {};
44910     this.addEvents({
44911         /**
44912          * @event layout
44913          * Fires when a layout is performed. 
44914          * @param {Roo.LayoutManager} this
44915          */
44916         "layout" : true,
44917         /**
44918          * @event regionresized
44919          * Fires when the user resizes a region. 
44920          * @param {Roo.LayoutRegion} region The resized region
44921          * @param {Number} newSize The new size (width for east/west, height for north/south)
44922          */
44923         "regionresized" : true,
44924         /**
44925          * @event regioncollapsed
44926          * Fires when a region is collapsed. 
44927          * @param {Roo.LayoutRegion} region The collapsed region
44928          */
44929         "regioncollapsed" : true,
44930         /**
44931          * @event regionexpanded
44932          * Fires when a region is expanded.  
44933          * @param {Roo.LayoutRegion} region The expanded region
44934          */
44935         "regionexpanded" : true
44936     });
44937     this.updating = false;
44938     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44939 };
44940
44941 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44942     /**
44943      * Returns true if this layout is currently being updated
44944      * @return {Boolean}
44945      */
44946     isUpdating : function(){
44947         return this.updating; 
44948     },
44949     
44950     /**
44951      * Suspend the LayoutManager from doing auto-layouts while
44952      * making multiple add or remove calls
44953      */
44954     beginUpdate : function(){
44955         this.updating = true;    
44956     },
44957     
44958     /**
44959      * Restore auto-layouts and optionally disable the manager from performing a layout
44960      * @param {Boolean} noLayout true to disable a layout update 
44961      */
44962     endUpdate : function(noLayout){
44963         this.updating = false;
44964         if(!noLayout){
44965             this.layout();
44966         }    
44967     },
44968     
44969     layout: function(){
44970         
44971     },
44972     
44973     onRegionResized : function(region, newSize){
44974         this.fireEvent("regionresized", region, newSize);
44975         this.layout();
44976     },
44977     
44978     onRegionCollapsed : function(region){
44979         this.fireEvent("regioncollapsed", region);
44980     },
44981     
44982     onRegionExpanded : function(region){
44983         this.fireEvent("regionexpanded", region);
44984     },
44985         
44986     /**
44987      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44988      * performs box-model adjustments.
44989      * @return {Object} The size as an object {width: (the width), height: (the height)}
44990      */
44991     getViewSize : function(){
44992         var size;
44993         if(this.el.dom != document.body){
44994             size = this.el.getSize();
44995         }else{
44996             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44997         }
44998         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44999         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45000         return size;
45001     },
45002     
45003     /**
45004      * Returns the Element this layout is bound to.
45005      * @return {Roo.Element}
45006      */
45007     getEl : function(){
45008         return this.el;
45009     },
45010     
45011     /**
45012      * Returns the specified region.
45013      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
45014      * @return {Roo.LayoutRegion}
45015      */
45016     getRegion : function(target){
45017         return this.regions[target.toLowerCase()];
45018     },
45019     
45020     onWindowResize : function(){
45021         if(this.monitorWindowResize){
45022             this.layout();
45023         }
45024     }
45025 });/*
45026  * Based on:
45027  * Ext JS Library 1.1.1
45028  * Copyright(c) 2006-2007, Ext JS, LLC.
45029  *
45030  * Originally Released Under LGPL - original licence link has changed is not relivant.
45031  *
45032  * Fork - LGPL
45033  * <script type="text/javascript">
45034  */
45035 /**
45036  * @class Roo.BorderLayout
45037  * @extends Roo.LayoutManager
45038  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
45039  * please see: <br><br>
45040  * <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>
45041  * <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>
45042  * Example:
45043  <pre><code>
45044  var layout = new Roo.BorderLayout(document.body, {
45045     north: {
45046         initialSize: 25,
45047         titlebar: false
45048     },
45049     west: {
45050         split:true,
45051         initialSize: 200,
45052         minSize: 175,
45053         maxSize: 400,
45054         titlebar: true,
45055         collapsible: true
45056     },
45057     east: {
45058         split:true,
45059         initialSize: 202,
45060         minSize: 175,
45061         maxSize: 400,
45062         titlebar: true,
45063         collapsible: true
45064     },
45065     south: {
45066         split:true,
45067         initialSize: 100,
45068         minSize: 100,
45069         maxSize: 200,
45070         titlebar: true,
45071         collapsible: true
45072     },
45073     center: {
45074         titlebar: true,
45075         autoScroll:true,
45076         resizeTabs: true,
45077         minTabWidth: 50,
45078         preferredTabWidth: 150
45079     }
45080 });
45081
45082 // shorthand
45083 var CP = Roo.ContentPanel;
45084
45085 layout.beginUpdate();
45086 layout.add("north", new CP("north", "North"));
45087 layout.add("south", new CP("south", {title: "South", closable: true}));
45088 layout.add("west", new CP("west", {title: "West"}));
45089 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
45090 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
45091 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
45092 layout.getRegion("center").showPanel("center1");
45093 layout.endUpdate();
45094 </code></pre>
45095
45096 <b>The container the layout is rendered into can be either the body element or any other element.
45097 If it is not the body element, the container needs to either be an absolute positioned element,
45098 or you will need to add "position:relative" to the css of the container.  You will also need to specify
45099 the container size if it is not the body element.</b>
45100
45101 * @constructor
45102 * Create a new BorderLayout
45103 * @param {String/HTMLElement/Element} container The container this layout is bound to
45104 * @param {Object} config Configuration options
45105  */
45106 Roo.BorderLayout = function(container, config){
45107     config = config || {};
45108     Roo.BorderLayout.superclass.constructor.call(this, container, config);
45109     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
45110     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
45111         var target = this.factory.validRegions[i];
45112         if(config[target]){
45113             this.addRegion(target, config[target]);
45114         }
45115     }
45116 };
45117
45118 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
45119     /**
45120      * Creates and adds a new region if it doesn't already exist.
45121      * @param {String} target The target region key (north, south, east, west or center).
45122      * @param {Object} config The regions config object
45123      * @return {BorderLayoutRegion} The new region
45124      */
45125     addRegion : function(target, config){
45126         if(!this.regions[target]){
45127             var r = this.factory.create(target, this, config);
45128             this.bindRegion(target, r);
45129         }
45130         return this.regions[target];
45131     },
45132
45133     // private (kinda)
45134     bindRegion : function(name, r){
45135         this.regions[name] = r;
45136         r.on("visibilitychange", this.layout, this);
45137         r.on("paneladded", this.layout, this);
45138         r.on("panelremoved", this.layout, this);
45139         r.on("invalidated", this.layout, this);
45140         r.on("resized", this.onRegionResized, this);
45141         r.on("collapsed", this.onRegionCollapsed, this);
45142         r.on("expanded", this.onRegionExpanded, this);
45143     },
45144
45145     /**
45146      * Performs a layout update.
45147      */
45148     layout : function(){
45149         if(this.updating) return;
45150         var size = this.getViewSize();
45151         var w = size.width;
45152         var h = size.height;
45153         var centerW = w;
45154         var centerH = h;
45155         var centerY = 0;
45156         var centerX = 0;
45157         //var x = 0, y = 0;
45158
45159         var rs = this.regions;
45160         var north = rs["north"];
45161         var south = rs["south"]; 
45162         var west = rs["west"];
45163         var east = rs["east"];
45164         var center = rs["center"];
45165         //if(this.hideOnLayout){ // not supported anymore
45166             //c.el.setStyle("display", "none");
45167         //}
45168         if(north && north.isVisible()){
45169             var b = north.getBox();
45170             var m = north.getMargins();
45171             b.width = w - (m.left+m.right);
45172             b.x = m.left;
45173             b.y = m.top;
45174             centerY = b.height + b.y + m.bottom;
45175             centerH -= centerY;
45176             north.updateBox(this.safeBox(b));
45177         }
45178         if(south && south.isVisible()){
45179             var b = south.getBox();
45180             var m = south.getMargins();
45181             b.width = w - (m.left+m.right);
45182             b.x = m.left;
45183             var totalHeight = (b.height + m.top + m.bottom);
45184             b.y = h - totalHeight + m.top;
45185             centerH -= totalHeight;
45186             south.updateBox(this.safeBox(b));
45187         }
45188         if(west && west.isVisible()){
45189             var b = west.getBox();
45190             var m = west.getMargins();
45191             b.height = centerH - (m.top+m.bottom);
45192             b.x = m.left;
45193             b.y = centerY + m.top;
45194             var totalWidth = (b.width + m.left + m.right);
45195             centerX += totalWidth;
45196             centerW -= totalWidth;
45197             west.updateBox(this.safeBox(b));
45198         }
45199         if(east && east.isVisible()){
45200             var b = east.getBox();
45201             var m = east.getMargins();
45202             b.height = centerH - (m.top+m.bottom);
45203             var totalWidth = (b.width + m.left + m.right);
45204             b.x = w - totalWidth + m.left;
45205             b.y = centerY + m.top;
45206             centerW -= totalWidth;
45207             east.updateBox(this.safeBox(b));
45208         }
45209         if(center){
45210             var m = center.getMargins();
45211             var centerBox = {
45212                 x: centerX + m.left,
45213                 y: centerY + m.top,
45214                 width: centerW - (m.left+m.right),
45215                 height: centerH - (m.top+m.bottom)
45216             };
45217             //if(this.hideOnLayout){
45218                 //center.el.setStyle("display", "block");
45219             //}
45220             center.updateBox(this.safeBox(centerBox));
45221         }
45222         this.el.repaint();
45223         this.fireEvent("layout", this);
45224     },
45225
45226     // private
45227     safeBox : function(box){
45228         box.width = Math.max(0, box.width);
45229         box.height = Math.max(0, box.height);
45230         return box;
45231     },
45232
45233     /**
45234      * Adds a ContentPanel (or subclass) to this layout.
45235      * @param {String} target The target region key (north, south, east, west or center).
45236      * @param {Roo.ContentPanel} panel The panel to add
45237      * @return {Roo.ContentPanel} The added panel
45238      */
45239     add : function(target, panel){
45240          
45241         target = target.toLowerCase();
45242         return this.regions[target].add(panel);
45243     },
45244
45245     /**
45246      * Remove a ContentPanel (or subclass) to this layout.
45247      * @param {String} target The target region key (north, south, east, west or center).
45248      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
45249      * @return {Roo.ContentPanel} The removed panel
45250      */
45251     remove : function(target, panel){
45252         target = target.toLowerCase();
45253         return this.regions[target].remove(panel);
45254     },
45255
45256     /**
45257      * Searches all regions for a panel with the specified id
45258      * @param {String} panelId
45259      * @return {Roo.ContentPanel} The panel or null if it wasn't found
45260      */
45261     findPanel : function(panelId){
45262         var rs = this.regions;
45263         for(var target in rs){
45264             if(typeof rs[target] != "function"){
45265                 var p = rs[target].getPanel(panelId);
45266                 if(p){
45267                     return p;
45268                 }
45269             }
45270         }
45271         return null;
45272     },
45273
45274     /**
45275      * Searches all regions for a panel with the specified id and activates (shows) it.
45276      * @param {String/ContentPanel} panelId The panels id or the panel itself
45277      * @return {Roo.ContentPanel} The shown panel or null
45278      */
45279     showPanel : function(panelId) {
45280       var rs = this.regions;
45281       for(var target in rs){
45282          var r = rs[target];
45283          if(typeof r != "function"){
45284             if(r.hasPanel(panelId)){
45285                return r.showPanel(panelId);
45286             }
45287          }
45288       }
45289       return null;
45290    },
45291
45292    /**
45293      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
45294      * @param {Roo.state.Provider} provider (optional) An alternate state provider
45295      */
45296     restoreState : function(provider){
45297         if(!provider){
45298             provider = Roo.state.Manager;
45299         }
45300         var sm = new Roo.LayoutStateManager();
45301         sm.init(this, provider);
45302     },
45303
45304     /**
45305      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
45306      * object should contain properties for each region to add ContentPanels to, and each property's value should be
45307      * a valid ContentPanel config object.  Example:
45308      * <pre><code>
45309 // Create the main layout
45310 var layout = new Roo.BorderLayout('main-ct', {
45311     west: {
45312         split:true,
45313         minSize: 175,
45314         titlebar: true
45315     },
45316     center: {
45317         title:'Components'
45318     }
45319 }, 'main-ct');
45320
45321 // Create and add multiple ContentPanels at once via configs
45322 layout.batchAdd({
45323    west: {
45324        id: 'source-files',
45325        autoCreate:true,
45326        title:'Ext Source Files',
45327        autoScroll:true,
45328        fitToFrame:true
45329    },
45330    center : {
45331        el: cview,
45332        autoScroll:true,
45333        fitToFrame:true,
45334        toolbar: tb,
45335        resizeEl:'cbody'
45336    }
45337 });
45338 </code></pre>
45339      * @param {Object} regions An object containing ContentPanel configs by region name
45340      */
45341     batchAdd : function(regions){
45342         this.beginUpdate();
45343         for(var rname in regions){
45344             var lr = this.regions[rname];
45345             if(lr){
45346                 this.addTypedPanels(lr, regions[rname]);
45347             }
45348         }
45349         this.endUpdate();
45350     },
45351
45352     // private
45353     addTypedPanels : function(lr, ps){
45354         if(typeof ps == 'string'){
45355             lr.add(new Roo.ContentPanel(ps));
45356         }
45357         else if(ps instanceof Array){
45358             for(var i =0, len = ps.length; i < len; i++){
45359                 this.addTypedPanels(lr, ps[i]);
45360             }
45361         }
45362         else if(!ps.events){ // raw config?
45363             var el = ps.el;
45364             delete ps.el; // prevent conflict
45365             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
45366         }
45367         else {  // panel object assumed!
45368             lr.add(ps);
45369         }
45370     },
45371     /**
45372      * Adds a xtype elements to the layout.
45373      * <pre><code>
45374
45375 layout.addxtype({
45376        xtype : 'ContentPanel',
45377        region: 'west',
45378        items: [ .... ]
45379    }
45380 );
45381
45382 layout.addxtype({
45383         xtype : 'NestedLayoutPanel',
45384         region: 'west',
45385         layout: {
45386            center: { },
45387            west: { }   
45388         },
45389         items : [ ... list of content panels or nested layout panels.. ]
45390    }
45391 );
45392 </code></pre>
45393      * @param {Object} cfg Xtype definition of item to add.
45394      */
45395     addxtype : function(cfg)
45396     {
45397         // basically accepts a pannel...
45398         // can accept a layout region..!?!?
45399         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
45400         
45401         if (!cfg.xtype.match(/Panel$/)) {
45402             return false;
45403         }
45404         var ret = false;
45405         
45406         if (typeof(cfg.region) == 'undefined') {
45407             Roo.log("Failed to add Panel, region was not set");
45408             Roo.log(cfg);
45409             return false;
45410         }
45411         var region = cfg.region;
45412         delete cfg.region;
45413         
45414           
45415         var xitems = [];
45416         if (cfg.items) {
45417             xitems = cfg.items;
45418             delete cfg.items;
45419         }
45420         var nb = false;
45421         
45422         switch(cfg.xtype) 
45423         {
45424             case 'ContentPanel':  // ContentPanel (el, cfg)
45425             case 'ScrollPanel':  // ContentPanel (el, cfg)
45426                 if(cfg.autoCreate) {
45427                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
45428                 } else {
45429                     var el = this.el.createChild();
45430                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
45431                 }
45432                 
45433                 this.add(region, ret);
45434                 break;
45435             
45436             
45437             case 'TreePanel': // our new panel!
45438                 cfg.el = this.el.createChild();
45439                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
45440                 this.add(region, ret);
45441                 break;
45442             
45443             case 'NestedLayoutPanel': 
45444                 // create a new Layout (which is  a Border Layout...
45445                 var el = this.el.createChild();
45446                 var clayout = cfg.layout;
45447                 delete cfg.layout;
45448                 clayout.items   = clayout.items  || [];
45449                 // replace this exitems with the clayout ones..
45450                 xitems = clayout.items;
45451                  
45452                 
45453                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
45454                     cfg.background = false;
45455                 }
45456                 var layout = new Roo.BorderLayout(el, clayout);
45457                 
45458                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
45459                 //console.log('adding nested layout panel '  + cfg.toSource());
45460                 this.add(region, ret);
45461                 nb = {}; /// find first...
45462                 break;
45463                 
45464             case 'GridPanel': 
45465             
45466                 // needs grid and region
45467                 
45468                 //var el = this.getRegion(region).el.createChild();
45469                 var el = this.el.createChild();
45470                 // create the grid first...
45471                 
45472                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
45473                 delete cfg.grid;
45474                 if (region == 'center' && this.active ) {
45475                     cfg.background = false;
45476                 }
45477                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
45478                 
45479                 this.add(region, ret);
45480                 if (cfg.background) {
45481                     ret.on('activate', function(gp) {
45482                         if (!gp.grid.rendered) {
45483                             gp.grid.render();
45484                         }
45485                     });
45486                 } else {
45487                     grid.render();
45488                 }
45489                 break;
45490            
45491                
45492                 
45493                 
45494             default: 
45495                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
45496                 return null;
45497              // GridPanel (grid, cfg)
45498             
45499         }
45500         this.beginUpdate();
45501         // add children..
45502         var region = '';
45503         var abn = {};
45504         Roo.each(xitems, function(i)  {
45505             region = nb && i.region ? i.region : false;
45506             
45507             var add = ret.addxtype(i);
45508            
45509             if (region) {
45510                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
45511                 if (!i.background) {
45512                     abn[region] = nb[region] ;
45513                 }
45514             }
45515             
45516         });
45517         this.endUpdate();
45518
45519         // make the last non-background panel active..
45520         //if (nb) { Roo.log(abn); }
45521         if (nb) {
45522             
45523             for(var r in abn) {
45524                 region = this.getRegion(r);
45525                 if (region) {
45526                     // tried using nb[r], but it does not work..
45527                      
45528                     region.showPanel(abn[r]);
45529                    
45530                 }
45531             }
45532         }
45533         return ret;
45534         
45535     }
45536 });
45537
45538 /**
45539  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
45540  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
45541  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
45542  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
45543  * <pre><code>
45544 // shorthand
45545 var CP = Roo.ContentPanel;
45546
45547 var layout = Roo.BorderLayout.create({
45548     north: {
45549         initialSize: 25,
45550         titlebar: false,
45551         panels: [new CP("north", "North")]
45552     },
45553     west: {
45554         split:true,
45555         initialSize: 200,
45556         minSize: 175,
45557         maxSize: 400,
45558         titlebar: true,
45559         collapsible: true,
45560         panels: [new CP("west", {title: "West"})]
45561     },
45562     east: {
45563         split:true,
45564         initialSize: 202,
45565         minSize: 175,
45566         maxSize: 400,
45567         titlebar: true,
45568         collapsible: true,
45569         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
45570     },
45571     south: {
45572         split:true,
45573         initialSize: 100,
45574         minSize: 100,
45575         maxSize: 200,
45576         titlebar: true,
45577         collapsible: true,
45578         panels: [new CP("south", {title: "South", closable: true})]
45579     },
45580     center: {
45581         titlebar: true,
45582         autoScroll:true,
45583         resizeTabs: true,
45584         minTabWidth: 50,
45585         preferredTabWidth: 150,
45586         panels: [
45587             new CP("center1", {title: "Close Me", closable: true}),
45588             new CP("center2", {title: "Center Panel", closable: false})
45589         ]
45590     }
45591 }, document.body);
45592
45593 layout.getRegion("center").showPanel("center1");
45594 </code></pre>
45595  * @param config
45596  * @param targetEl
45597  */
45598 Roo.BorderLayout.create = function(config, targetEl){
45599     var layout = new Roo.BorderLayout(targetEl || document.body, config);
45600     layout.beginUpdate();
45601     var regions = Roo.BorderLayout.RegionFactory.validRegions;
45602     for(var j = 0, jlen = regions.length; j < jlen; j++){
45603         var lr = regions[j];
45604         if(layout.regions[lr] && config[lr].panels){
45605             var r = layout.regions[lr];
45606             var ps = config[lr].panels;
45607             layout.addTypedPanels(r, ps);
45608         }
45609     }
45610     layout.endUpdate();
45611     return layout;
45612 };
45613
45614 // private
45615 Roo.BorderLayout.RegionFactory = {
45616     // private
45617     validRegions : ["north","south","east","west","center"],
45618
45619     // private
45620     create : function(target, mgr, config){
45621         target = target.toLowerCase();
45622         if(config.lightweight || config.basic){
45623             return new Roo.BasicLayoutRegion(mgr, config, target);
45624         }
45625         switch(target){
45626             case "north":
45627                 return new Roo.NorthLayoutRegion(mgr, config);
45628             case "south":
45629                 return new Roo.SouthLayoutRegion(mgr, config);
45630             case "east":
45631                 return new Roo.EastLayoutRegion(mgr, config);
45632             case "west":
45633                 return new Roo.WestLayoutRegion(mgr, config);
45634             case "center":
45635                 return new Roo.CenterLayoutRegion(mgr, config);
45636         }
45637         throw 'Layout region "'+target+'" not supported.';
45638     }
45639 };/*
45640  * Based on:
45641  * Ext JS Library 1.1.1
45642  * Copyright(c) 2006-2007, Ext JS, LLC.
45643  *
45644  * Originally Released Under LGPL - original licence link has changed is not relivant.
45645  *
45646  * Fork - LGPL
45647  * <script type="text/javascript">
45648  */
45649  
45650 /**
45651  * @class Roo.BasicLayoutRegion
45652  * @extends Roo.util.Observable
45653  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45654  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45655  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45656  */
45657 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45658     this.mgr = mgr;
45659     this.position  = pos;
45660     this.events = {
45661         /**
45662          * @scope Roo.BasicLayoutRegion
45663          */
45664         
45665         /**
45666          * @event beforeremove
45667          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45668          * @param {Roo.LayoutRegion} this
45669          * @param {Roo.ContentPanel} panel The panel
45670          * @param {Object} e The cancel event object
45671          */
45672         "beforeremove" : true,
45673         /**
45674          * @event invalidated
45675          * Fires when the layout for this region is changed.
45676          * @param {Roo.LayoutRegion} this
45677          */
45678         "invalidated" : true,
45679         /**
45680          * @event visibilitychange
45681          * Fires when this region is shown or hidden 
45682          * @param {Roo.LayoutRegion} this
45683          * @param {Boolean} visibility true or false
45684          */
45685         "visibilitychange" : true,
45686         /**
45687          * @event paneladded
45688          * Fires when a panel is added. 
45689          * @param {Roo.LayoutRegion} this
45690          * @param {Roo.ContentPanel} panel The panel
45691          */
45692         "paneladded" : true,
45693         /**
45694          * @event panelremoved
45695          * Fires when a panel is removed. 
45696          * @param {Roo.LayoutRegion} this
45697          * @param {Roo.ContentPanel} panel The panel
45698          */
45699         "panelremoved" : true,
45700         /**
45701          * @event collapsed
45702          * Fires when this region is collapsed.
45703          * @param {Roo.LayoutRegion} this
45704          */
45705         "collapsed" : true,
45706         /**
45707          * @event expanded
45708          * Fires when this region is expanded.
45709          * @param {Roo.LayoutRegion} this
45710          */
45711         "expanded" : true,
45712         /**
45713          * @event slideshow
45714          * Fires when this region is slid into view.
45715          * @param {Roo.LayoutRegion} this
45716          */
45717         "slideshow" : true,
45718         /**
45719          * @event slidehide
45720          * Fires when this region slides out of view. 
45721          * @param {Roo.LayoutRegion} this
45722          */
45723         "slidehide" : true,
45724         /**
45725          * @event panelactivated
45726          * Fires when a panel is activated. 
45727          * @param {Roo.LayoutRegion} this
45728          * @param {Roo.ContentPanel} panel The activated panel
45729          */
45730         "panelactivated" : true,
45731         /**
45732          * @event resized
45733          * Fires when the user resizes this region. 
45734          * @param {Roo.LayoutRegion} this
45735          * @param {Number} newSize The new size (width for east/west, height for north/south)
45736          */
45737         "resized" : true
45738     };
45739     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45740     this.panels = new Roo.util.MixedCollection();
45741     this.panels.getKey = this.getPanelId.createDelegate(this);
45742     this.box = null;
45743     this.activePanel = null;
45744     // ensure listeners are added...
45745     
45746     if (config.listeners || config.events) {
45747         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45748             listeners : config.listeners || {},
45749             events : config.events || {}
45750         });
45751     }
45752     
45753     if(skipConfig !== true){
45754         this.applyConfig(config);
45755     }
45756 };
45757
45758 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45759     getPanelId : function(p){
45760         return p.getId();
45761     },
45762     
45763     applyConfig : function(config){
45764         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45765         this.config = config;
45766         
45767     },
45768     
45769     /**
45770      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45771      * the width, for horizontal (north, south) the height.
45772      * @param {Number} newSize The new width or height
45773      */
45774     resizeTo : function(newSize){
45775         var el = this.el ? this.el :
45776                  (this.activePanel ? this.activePanel.getEl() : null);
45777         if(el){
45778             switch(this.position){
45779                 case "east":
45780                 case "west":
45781                     el.setWidth(newSize);
45782                     this.fireEvent("resized", this, newSize);
45783                 break;
45784                 case "north":
45785                 case "south":
45786                     el.setHeight(newSize);
45787                     this.fireEvent("resized", this, newSize);
45788                 break;                
45789             }
45790         }
45791     },
45792     
45793     getBox : function(){
45794         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45795     },
45796     
45797     getMargins : function(){
45798         return this.margins;
45799     },
45800     
45801     updateBox : function(box){
45802         this.box = box;
45803         var el = this.activePanel.getEl();
45804         el.dom.style.left = box.x + "px";
45805         el.dom.style.top = box.y + "px";
45806         this.activePanel.setSize(box.width, box.height);
45807     },
45808     
45809     /**
45810      * Returns the container element for this region.
45811      * @return {Roo.Element}
45812      */
45813     getEl : function(){
45814         return this.activePanel;
45815     },
45816     
45817     /**
45818      * Returns true if this region is currently visible.
45819      * @return {Boolean}
45820      */
45821     isVisible : function(){
45822         return this.activePanel ? true : false;
45823     },
45824     
45825     setActivePanel : function(panel){
45826         panel = this.getPanel(panel);
45827         if(this.activePanel && this.activePanel != panel){
45828             this.activePanel.setActiveState(false);
45829             this.activePanel.getEl().setLeftTop(-10000,-10000);
45830         }
45831         this.activePanel = panel;
45832         panel.setActiveState(true);
45833         if(this.box){
45834             panel.setSize(this.box.width, this.box.height);
45835         }
45836         this.fireEvent("panelactivated", this, panel);
45837         this.fireEvent("invalidated");
45838     },
45839     
45840     /**
45841      * Show the specified panel.
45842      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45843      * @return {Roo.ContentPanel} The shown panel or null
45844      */
45845     showPanel : function(panel){
45846         if(panel = this.getPanel(panel)){
45847             this.setActivePanel(panel);
45848         }
45849         return panel;
45850     },
45851     
45852     /**
45853      * Get the active panel for this region.
45854      * @return {Roo.ContentPanel} The active panel or null
45855      */
45856     getActivePanel : function(){
45857         return this.activePanel;
45858     },
45859     
45860     /**
45861      * Add the passed ContentPanel(s)
45862      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45863      * @return {Roo.ContentPanel} The panel added (if only one was added)
45864      */
45865     add : function(panel){
45866         if(arguments.length > 1){
45867             for(var i = 0, len = arguments.length; i < len; i++) {
45868                 this.add(arguments[i]);
45869             }
45870             return null;
45871         }
45872         if(this.hasPanel(panel)){
45873             this.showPanel(panel);
45874             return panel;
45875         }
45876         var el = panel.getEl();
45877         if(el.dom.parentNode != this.mgr.el.dom){
45878             this.mgr.el.dom.appendChild(el.dom);
45879         }
45880         if(panel.setRegion){
45881             panel.setRegion(this);
45882         }
45883         this.panels.add(panel);
45884         el.setStyle("position", "absolute");
45885         if(!panel.background){
45886             this.setActivePanel(panel);
45887             if(this.config.initialSize && this.panels.getCount()==1){
45888                 this.resizeTo(this.config.initialSize);
45889             }
45890         }
45891         this.fireEvent("paneladded", this, panel);
45892         return panel;
45893     },
45894     
45895     /**
45896      * Returns true if the panel is in this region.
45897      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45898      * @return {Boolean}
45899      */
45900     hasPanel : function(panel){
45901         if(typeof panel == "object"){ // must be panel obj
45902             panel = panel.getId();
45903         }
45904         return this.getPanel(panel) ? true : false;
45905     },
45906     
45907     /**
45908      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45909      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45910      * @param {Boolean} preservePanel Overrides the config preservePanel option
45911      * @return {Roo.ContentPanel} The panel that was removed
45912      */
45913     remove : function(panel, preservePanel){
45914         panel = this.getPanel(panel);
45915         if(!panel){
45916             return null;
45917         }
45918         var e = {};
45919         this.fireEvent("beforeremove", this, panel, e);
45920         if(e.cancel === true){
45921             return null;
45922         }
45923         var panelId = panel.getId();
45924         this.panels.removeKey(panelId);
45925         return panel;
45926     },
45927     
45928     /**
45929      * Returns the panel specified or null if it's not in this region.
45930      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45931      * @return {Roo.ContentPanel}
45932      */
45933     getPanel : function(id){
45934         if(typeof id == "object"){ // must be panel obj
45935             return id;
45936         }
45937         return this.panels.get(id);
45938     },
45939     
45940     /**
45941      * Returns this regions position (north/south/east/west/center).
45942      * @return {String} 
45943      */
45944     getPosition: function(){
45945         return this.position;    
45946     }
45947 });/*
45948  * Based on:
45949  * Ext JS Library 1.1.1
45950  * Copyright(c) 2006-2007, Ext JS, LLC.
45951  *
45952  * Originally Released Under LGPL - original licence link has changed is not relivant.
45953  *
45954  * Fork - LGPL
45955  * <script type="text/javascript">
45956  */
45957  
45958 /**
45959  * @class Roo.LayoutRegion
45960  * @extends Roo.BasicLayoutRegion
45961  * This class represents a region in a layout manager.
45962  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45963  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45964  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45965  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45966  * @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})
45967  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45968  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45969  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45970  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45971  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45972  * @cfg {String}    title           The title for the region (overrides panel titles)
45973  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45974  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45975  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45976  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45977  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45978  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45979  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45980  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45981  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45982  * @cfg {Boolean}   showPin         True to show a pin button
45983  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45984  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45985  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45986  * @cfg {Number}    width           For East/West panels
45987  * @cfg {Number}    height          For North/South panels
45988  * @cfg {Boolean}   split           To show the splitter
45989  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45990  */
45991 Roo.LayoutRegion = function(mgr, config, pos){
45992     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45993     var dh = Roo.DomHelper;
45994     /** This region's container element 
45995     * @type Roo.Element */
45996     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45997     /** This region's title element 
45998     * @type Roo.Element */
45999
46000     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
46001         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
46002         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
46003     ]}, true);
46004     this.titleEl.enableDisplayMode();
46005     /** This region's title text element 
46006     * @type HTMLElement */
46007     this.titleTextEl = this.titleEl.dom.firstChild;
46008     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
46009     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
46010     this.closeBtn.enableDisplayMode();
46011     this.closeBtn.on("click", this.closeClicked, this);
46012     this.closeBtn.hide();
46013
46014     this.createBody(config);
46015     this.visible = true;
46016     this.collapsed = false;
46017
46018     if(config.hideWhenEmpty){
46019         this.hide();
46020         this.on("paneladded", this.validateVisibility, this);
46021         this.on("panelremoved", this.validateVisibility, this);
46022     }
46023     this.applyConfig(config);
46024 };
46025
46026 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
46027
46028     createBody : function(){
46029         /** This region's body element 
46030         * @type Roo.Element */
46031         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
46032     },
46033
46034     applyConfig : function(c){
46035         if(c.collapsible && this.position != "center" && !this.collapsedEl){
46036             var dh = Roo.DomHelper;
46037             if(c.titlebar !== false){
46038                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
46039                 this.collapseBtn.on("click", this.collapse, this);
46040                 this.collapseBtn.enableDisplayMode();
46041
46042                 if(c.showPin === true || this.showPin){
46043                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
46044                     this.stickBtn.enableDisplayMode();
46045                     this.stickBtn.on("click", this.expand, this);
46046                     this.stickBtn.hide();
46047                 }
46048             }
46049             /** This region's collapsed element
46050             * @type Roo.Element */
46051             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
46052                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
46053             ]}, true);
46054             if(c.floatable !== false){
46055                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
46056                this.collapsedEl.on("click", this.collapseClick, this);
46057             }
46058
46059             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
46060                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
46061                    id: "message", unselectable: "on", style:{"float":"left"}});
46062                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
46063              }
46064             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
46065             this.expandBtn.on("click", this.expand, this);
46066         }
46067         if(this.collapseBtn){
46068             this.collapseBtn.setVisible(c.collapsible == true);
46069         }
46070         this.cmargins = c.cmargins || this.cmargins ||
46071                          (this.position == "west" || this.position == "east" ?
46072                              {top: 0, left: 2, right:2, bottom: 0} :
46073                              {top: 2, left: 0, right:0, bottom: 2});
46074         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
46075         this.bottomTabs = c.tabPosition != "top";
46076         this.autoScroll = c.autoScroll || false;
46077         if(this.autoScroll){
46078             this.bodyEl.setStyle("overflow", "auto");
46079         }else{
46080             this.bodyEl.setStyle("overflow", "hidden");
46081         }
46082         //if(c.titlebar !== false){
46083             if((!c.titlebar && !c.title) || c.titlebar === false){
46084                 this.titleEl.hide();
46085             }else{
46086                 this.titleEl.show();
46087                 if(c.title){
46088                     this.titleTextEl.innerHTML = c.title;
46089                 }
46090             }
46091         //}
46092         this.duration = c.duration || .30;
46093         this.slideDuration = c.slideDuration || .45;
46094         this.config = c;
46095         if(c.collapsed){
46096             this.collapse(true);
46097         }
46098         if(c.hidden){
46099             this.hide();
46100         }
46101     },
46102     /**
46103      * Returns true if this region is currently visible.
46104      * @return {Boolean}
46105      */
46106     isVisible : function(){
46107         return this.visible;
46108     },
46109
46110     /**
46111      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
46112      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
46113      */
46114     setCollapsedTitle : function(title){
46115         title = title || "&#160;";
46116         if(this.collapsedTitleTextEl){
46117             this.collapsedTitleTextEl.innerHTML = title;
46118         }
46119     },
46120
46121     getBox : function(){
46122         var b;
46123         if(!this.collapsed){
46124             b = this.el.getBox(false, true);
46125         }else{
46126             b = this.collapsedEl.getBox(false, true);
46127         }
46128         return b;
46129     },
46130
46131     getMargins : function(){
46132         return this.collapsed ? this.cmargins : this.margins;
46133     },
46134
46135     highlight : function(){
46136         this.el.addClass("x-layout-panel-dragover");
46137     },
46138
46139     unhighlight : function(){
46140         this.el.removeClass("x-layout-panel-dragover");
46141     },
46142
46143     updateBox : function(box){
46144         this.box = box;
46145         if(!this.collapsed){
46146             this.el.dom.style.left = box.x + "px";
46147             this.el.dom.style.top = box.y + "px";
46148             this.updateBody(box.width, box.height);
46149         }else{
46150             this.collapsedEl.dom.style.left = box.x + "px";
46151             this.collapsedEl.dom.style.top = box.y + "px";
46152             this.collapsedEl.setSize(box.width, box.height);
46153         }
46154         if(this.tabs){
46155             this.tabs.autoSizeTabs();
46156         }
46157     },
46158
46159     updateBody : function(w, h){
46160         if(w !== null){
46161             this.el.setWidth(w);
46162             w -= this.el.getBorderWidth("rl");
46163             if(this.config.adjustments){
46164                 w += this.config.adjustments[0];
46165             }
46166         }
46167         if(h !== null){
46168             this.el.setHeight(h);
46169             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
46170             h -= this.el.getBorderWidth("tb");
46171             if(this.config.adjustments){
46172                 h += this.config.adjustments[1];
46173             }
46174             this.bodyEl.setHeight(h);
46175             if(this.tabs){
46176                 h = this.tabs.syncHeight(h);
46177             }
46178         }
46179         if(this.panelSize){
46180             w = w !== null ? w : this.panelSize.width;
46181             h = h !== null ? h : this.panelSize.height;
46182         }
46183         if(this.activePanel){
46184             var el = this.activePanel.getEl();
46185             w = w !== null ? w : el.getWidth();
46186             h = h !== null ? h : el.getHeight();
46187             this.panelSize = {width: w, height: h};
46188             this.activePanel.setSize(w, h);
46189         }
46190         if(Roo.isIE && this.tabs){
46191             this.tabs.el.repaint();
46192         }
46193     },
46194
46195     /**
46196      * Returns the container element for this region.
46197      * @return {Roo.Element}
46198      */
46199     getEl : function(){
46200         return this.el;
46201     },
46202
46203     /**
46204      * Hides this region.
46205      */
46206     hide : function(){
46207         if(!this.collapsed){
46208             this.el.dom.style.left = "-2000px";
46209             this.el.hide();
46210         }else{
46211             this.collapsedEl.dom.style.left = "-2000px";
46212             this.collapsedEl.hide();
46213         }
46214         this.visible = false;
46215         this.fireEvent("visibilitychange", this, false);
46216     },
46217
46218     /**
46219      * Shows this region if it was previously hidden.
46220      */
46221     show : function(){
46222         if(!this.collapsed){
46223             this.el.show();
46224         }else{
46225             this.collapsedEl.show();
46226         }
46227         this.visible = true;
46228         this.fireEvent("visibilitychange", this, true);
46229     },
46230
46231     closeClicked : function(){
46232         if(this.activePanel){
46233             this.remove(this.activePanel);
46234         }
46235     },
46236
46237     collapseClick : function(e){
46238         if(this.isSlid){
46239            e.stopPropagation();
46240            this.slideIn();
46241         }else{
46242            e.stopPropagation();
46243            this.slideOut();
46244         }
46245     },
46246
46247     /**
46248      * Collapses this region.
46249      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
46250      */
46251     collapse : function(skipAnim){
46252         if(this.collapsed) return;
46253         this.collapsed = true;
46254         if(this.split){
46255             this.split.el.hide();
46256         }
46257         if(this.config.animate && skipAnim !== true){
46258             this.fireEvent("invalidated", this);
46259             this.animateCollapse();
46260         }else{
46261             this.el.setLocation(-20000,-20000);
46262             this.el.hide();
46263             this.collapsedEl.show();
46264             this.fireEvent("collapsed", this);
46265             this.fireEvent("invalidated", this);
46266         }
46267     },
46268
46269     animateCollapse : function(){
46270         // overridden
46271     },
46272
46273     /**
46274      * Expands this region if it was previously collapsed.
46275      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
46276      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
46277      */
46278     expand : function(e, skipAnim){
46279         if(e) e.stopPropagation();
46280         if(!this.collapsed || this.el.hasActiveFx()) return;
46281         if(this.isSlid){
46282             this.afterSlideIn();
46283             skipAnim = true;
46284         }
46285         this.collapsed = false;
46286         if(this.config.animate && skipAnim !== true){
46287             this.animateExpand();
46288         }else{
46289             this.el.show();
46290             if(this.split){
46291                 this.split.el.show();
46292             }
46293             this.collapsedEl.setLocation(-2000,-2000);
46294             this.collapsedEl.hide();
46295             this.fireEvent("invalidated", this);
46296             this.fireEvent("expanded", this);
46297         }
46298     },
46299
46300     animateExpand : function(){
46301         // overridden
46302     },
46303
46304     initTabs : function()
46305     {
46306         this.bodyEl.setStyle("overflow", "hidden");
46307         var ts = new Roo.TabPanel(
46308                 this.bodyEl.dom,
46309                 {
46310                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
46311                     disableTooltips: this.config.disableTabTips,
46312                     toolbar : this.config.toolbar
46313                 }
46314         );
46315         if(this.config.hideTabs){
46316             ts.stripWrap.setDisplayed(false);
46317         }
46318         this.tabs = ts;
46319         ts.resizeTabs = this.config.resizeTabs === true;
46320         ts.minTabWidth = this.config.minTabWidth || 40;
46321         ts.maxTabWidth = this.config.maxTabWidth || 250;
46322         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
46323         ts.monitorResize = false;
46324         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46325         ts.bodyEl.addClass('x-layout-tabs-body');
46326         this.panels.each(this.initPanelAsTab, this);
46327     },
46328
46329     initPanelAsTab : function(panel){
46330         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
46331                     this.config.closeOnTab && panel.isClosable());
46332         if(panel.tabTip !== undefined){
46333             ti.setTooltip(panel.tabTip);
46334         }
46335         ti.on("activate", function(){
46336               this.setActivePanel(panel);
46337         }, this);
46338         if(this.config.closeOnTab){
46339             ti.on("beforeclose", function(t, e){
46340                 e.cancel = true;
46341                 this.remove(panel);
46342             }, this);
46343         }
46344         return ti;
46345     },
46346
46347     updatePanelTitle : function(panel, title){
46348         if(this.activePanel == panel){
46349             this.updateTitle(title);
46350         }
46351         if(this.tabs){
46352             var ti = this.tabs.getTab(panel.getEl().id);
46353             ti.setText(title);
46354             if(panel.tabTip !== undefined){
46355                 ti.setTooltip(panel.tabTip);
46356             }
46357         }
46358     },
46359
46360     updateTitle : function(title){
46361         if(this.titleTextEl && !this.config.title){
46362             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
46363         }
46364     },
46365
46366     setActivePanel : function(panel){
46367         panel = this.getPanel(panel);
46368         if(this.activePanel && this.activePanel != panel){
46369             this.activePanel.setActiveState(false);
46370         }
46371         this.activePanel = panel;
46372         panel.setActiveState(true);
46373         if(this.panelSize){
46374             panel.setSize(this.panelSize.width, this.panelSize.height);
46375         }
46376         if(this.closeBtn){
46377             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
46378         }
46379         this.updateTitle(panel.getTitle());
46380         if(this.tabs){
46381             this.fireEvent("invalidated", this);
46382         }
46383         this.fireEvent("panelactivated", this, panel);
46384     },
46385
46386     /**
46387      * Shows the specified panel.
46388      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
46389      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
46390      */
46391     showPanel : function(panel){
46392         if(panel = this.getPanel(panel)){
46393             if(this.tabs){
46394                 var tab = this.tabs.getTab(panel.getEl().id);
46395                 if(tab.isHidden()){
46396                     this.tabs.unhideTab(tab.id);
46397                 }
46398                 tab.activate();
46399             }else{
46400                 this.setActivePanel(panel);
46401             }
46402         }
46403         return panel;
46404     },
46405
46406     /**
46407      * Get the active panel for this region.
46408      * @return {Roo.ContentPanel} The active panel or null
46409      */
46410     getActivePanel : function(){
46411         return this.activePanel;
46412     },
46413
46414     validateVisibility : function(){
46415         if(this.panels.getCount() < 1){
46416             this.updateTitle("&#160;");
46417             this.closeBtn.hide();
46418             this.hide();
46419         }else{
46420             if(!this.isVisible()){
46421                 this.show();
46422             }
46423         }
46424     },
46425
46426     /**
46427      * Adds the passed ContentPanel(s) to this region.
46428      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
46429      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
46430      */
46431     add : function(panel){
46432         if(arguments.length > 1){
46433             for(var i = 0, len = arguments.length; i < len; i++) {
46434                 this.add(arguments[i]);
46435             }
46436             return null;
46437         }
46438         if(this.hasPanel(panel)){
46439             this.showPanel(panel);
46440             return panel;
46441         }
46442         panel.setRegion(this);
46443         this.panels.add(panel);
46444         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
46445             this.bodyEl.dom.appendChild(panel.getEl().dom);
46446             if(panel.background !== true){
46447                 this.setActivePanel(panel);
46448             }
46449             this.fireEvent("paneladded", this, panel);
46450             return panel;
46451         }
46452         if(!this.tabs){
46453             this.initTabs();
46454         }else{
46455             this.initPanelAsTab(panel);
46456         }
46457         if(panel.background !== true){
46458             this.tabs.activate(panel.getEl().id);
46459         }
46460         this.fireEvent("paneladded", this, panel);
46461         return panel;
46462     },
46463
46464     /**
46465      * Hides the tab for the specified panel.
46466      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46467      */
46468     hidePanel : function(panel){
46469         if(this.tabs && (panel = this.getPanel(panel))){
46470             this.tabs.hideTab(panel.getEl().id);
46471         }
46472     },
46473
46474     /**
46475      * Unhides the tab for a previously hidden panel.
46476      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46477      */
46478     unhidePanel : function(panel){
46479         if(this.tabs && (panel = this.getPanel(panel))){
46480             this.tabs.unhideTab(panel.getEl().id);
46481         }
46482     },
46483
46484     clearPanels : function(){
46485         while(this.panels.getCount() > 0){
46486              this.remove(this.panels.first());
46487         }
46488     },
46489
46490     /**
46491      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46492      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46493      * @param {Boolean} preservePanel Overrides the config preservePanel option
46494      * @return {Roo.ContentPanel} The panel that was removed
46495      */
46496     remove : function(panel, preservePanel){
46497         panel = this.getPanel(panel);
46498         if(!panel){
46499             return null;
46500         }
46501         var e = {};
46502         this.fireEvent("beforeremove", this, panel, e);
46503         if(e.cancel === true){
46504             return null;
46505         }
46506         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
46507         var panelId = panel.getId();
46508         this.panels.removeKey(panelId);
46509         if(preservePanel){
46510             document.body.appendChild(panel.getEl().dom);
46511         }
46512         if(this.tabs){
46513             this.tabs.removeTab(panel.getEl().id);
46514         }else if (!preservePanel){
46515             this.bodyEl.dom.removeChild(panel.getEl().dom);
46516         }
46517         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
46518             var p = this.panels.first();
46519             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
46520             tempEl.appendChild(p.getEl().dom);
46521             this.bodyEl.update("");
46522             this.bodyEl.dom.appendChild(p.getEl().dom);
46523             tempEl = null;
46524             this.updateTitle(p.getTitle());
46525             this.tabs = null;
46526             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46527             this.setActivePanel(p);
46528         }
46529         panel.setRegion(null);
46530         if(this.activePanel == panel){
46531             this.activePanel = null;
46532         }
46533         if(this.config.autoDestroy !== false && preservePanel !== true){
46534             try{panel.destroy();}catch(e){}
46535         }
46536         this.fireEvent("panelremoved", this, panel);
46537         return panel;
46538     },
46539
46540     /**
46541      * Returns the TabPanel component used by this region
46542      * @return {Roo.TabPanel}
46543      */
46544     getTabs : function(){
46545         return this.tabs;
46546     },
46547
46548     createTool : function(parentEl, className){
46549         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
46550             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
46551         btn.addClassOnOver("x-layout-tools-button-over");
46552         return btn;
46553     }
46554 });/*
46555  * Based on:
46556  * Ext JS Library 1.1.1
46557  * Copyright(c) 2006-2007, Ext JS, LLC.
46558  *
46559  * Originally Released Under LGPL - original licence link has changed is not relivant.
46560  *
46561  * Fork - LGPL
46562  * <script type="text/javascript">
46563  */
46564  
46565
46566
46567 /**
46568  * @class Roo.SplitLayoutRegion
46569  * @extends Roo.LayoutRegion
46570  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
46571  */
46572 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
46573     this.cursor = cursor;
46574     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
46575 };
46576
46577 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
46578     splitTip : "Drag to resize.",
46579     collapsibleSplitTip : "Drag to resize. Double click to hide.",
46580     useSplitTips : false,
46581
46582     applyConfig : function(config){
46583         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
46584         if(config.split){
46585             if(!this.split){
46586                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
46587                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
46588                 /** The SplitBar for this region 
46589                 * @type Roo.SplitBar */
46590                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
46591                 this.split.on("moved", this.onSplitMove, this);
46592                 this.split.useShim = config.useShim === true;
46593                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
46594                 if(this.useSplitTips){
46595                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
46596                 }
46597                 if(config.collapsible){
46598                     this.split.el.on("dblclick", this.collapse,  this);
46599                 }
46600             }
46601             if(typeof config.minSize != "undefined"){
46602                 this.split.minSize = config.minSize;
46603             }
46604             if(typeof config.maxSize != "undefined"){
46605                 this.split.maxSize = config.maxSize;
46606             }
46607             if(config.hideWhenEmpty || config.hidden || config.collapsed){
46608                 this.hideSplitter();
46609             }
46610         }
46611     },
46612
46613     getHMaxSize : function(){
46614          var cmax = this.config.maxSize || 10000;
46615          var center = this.mgr.getRegion("center");
46616          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46617     },
46618
46619     getVMaxSize : function(){
46620          var cmax = this.config.maxSize || 10000;
46621          var center = this.mgr.getRegion("center");
46622          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46623     },
46624
46625     onSplitMove : function(split, newSize){
46626         this.fireEvent("resized", this, newSize);
46627     },
46628     
46629     /** 
46630      * Returns the {@link Roo.SplitBar} for this region.
46631      * @return {Roo.SplitBar}
46632      */
46633     getSplitBar : function(){
46634         return this.split;
46635     },
46636     
46637     hide : function(){
46638         this.hideSplitter();
46639         Roo.SplitLayoutRegion.superclass.hide.call(this);
46640     },
46641
46642     hideSplitter : function(){
46643         if(this.split){
46644             this.split.el.setLocation(-2000,-2000);
46645             this.split.el.hide();
46646         }
46647     },
46648
46649     show : function(){
46650         if(this.split){
46651             this.split.el.show();
46652         }
46653         Roo.SplitLayoutRegion.superclass.show.call(this);
46654     },
46655     
46656     beforeSlide: function(){
46657         if(Roo.isGecko){// firefox overflow auto bug workaround
46658             this.bodyEl.clip();
46659             if(this.tabs) this.tabs.bodyEl.clip();
46660             if(this.activePanel){
46661                 this.activePanel.getEl().clip();
46662                 
46663                 if(this.activePanel.beforeSlide){
46664                     this.activePanel.beforeSlide();
46665                 }
46666             }
46667         }
46668     },
46669     
46670     afterSlide : function(){
46671         if(Roo.isGecko){// firefox overflow auto bug workaround
46672             this.bodyEl.unclip();
46673             if(this.tabs) this.tabs.bodyEl.unclip();
46674             if(this.activePanel){
46675                 this.activePanel.getEl().unclip();
46676                 if(this.activePanel.afterSlide){
46677                     this.activePanel.afterSlide();
46678                 }
46679             }
46680         }
46681     },
46682
46683     initAutoHide : function(){
46684         if(this.autoHide !== false){
46685             if(!this.autoHideHd){
46686                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46687                 this.autoHideHd = {
46688                     "mouseout": function(e){
46689                         if(!e.within(this.el, true)){
46690                             st.delay(500);
46691                         }
46692                     },
46693                     "mouseover" : function(e){
46694                         st.cancel();
46695                     },
46696                     scope : this
46697                 };
46698             }
46699             this.el.on(this.autoHideHd);
46700         }
46701     },
46702
46703     clearAutoHide : function(){
46704         if(this.autoHide !== false){
46705             this.el.un("mouseout", this.autoHideHd.mouseout);
46706             this.el.un("mouseover", this.autoHideHd.mouseover);
46707         }
46708     },
46709
46710     clearMonitor : function(){
46711         Roo.get(document).un("click", this.slideInIf, this);
46712     },
46713
46714     // these names are backwards but not changed for compat
46715     slideOut : function(){
46716         if(this.isSlid || this.el.hasActiveFx()){
46717             return;
46718         }
46719         this.isSlid = true;
46720         if(this.collapseBtn){
46721             this.collapseBtn.hide();
46722         }
46723         this.closeBtnState = this.closeBtn.getStyle('display');
46724         this.closeBtn.hide();
46725         if(this.stickBtn){
46726             this.stickBtn.show();
46727         }
46728         this.el.show();
46729         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46730         this.beforeSlide();
46731         this.el.setStyle("z-index", 10001);
46732         this.el.slideIn(this.getSlideAnchor(), {
46733             callback: function(){
46734                 this.afterSlide();
46735                 this.initAutoHide();
46736                 Roo.get(document).on("click", this.slideInIf, this);
46737                 this.fireEvent("slideshow", this);
46738             },
46739             scope: this,
46740             block: true
46741         });
46742     },
46743
46744     afterSlideIn : function(){
46745         this.clearAutoHide();
46746         this.isSlid = false;
46747         this.clearMonitor();
46748         this.el.setStyle("z-index", "");
46749         if(this.collapseBtn){
46750             this.collapseBtn.show();
46751         }
46752         this.closeBtn.setStyle('display', this.closeBtnState);
46753         if(this.stickBtn){
46754             this.stickBtn.hide();
46755         }
46756         this.fireEvent("slidehide", this);
46757     },
46758
46759     slideIn : function(cb){
46760         if(!this.isSlid || this.el.hasActiveFx()){
46761             Roo.callback(cb);
46762             return;
46763         }
46764         this.isSlid = false;
46765         this.beforeSlide();
46766         this.el.slideOut(this.getSlideAnchor(), {
46767             callback: function(){
46768                 this.el.setLeftTop(-10000, -10000);
46769                 this.afterSlide();
46770                 this.afterSlideIn();
46771                 Roo.callback(cb);
46772             },
46773             scope: this,
46774             block: true
46775         });
46776     },
46777     
46778     slideInIf : function(e){
46779         if(!e.within(this.el)){
46780             this.slideIn();
46781         }
46782     },
46783
46784     animateCollapse : function(){
46785         this.beforeSlide();
46786         this.el.setStyle("z-index", 20000);
46787         var anchor = this.getSlideAnchor();
46788         this.el.slideOut(anchor, {
46789             callback : function(){
46790                 this.el.setStyle("z-index", "");
46791                 this.collapsedEl.slideIn(anchor, {duration:.3});
46792                 this.afterSlide();
46793                 this.el.setLocation(-10000,-10000);
46794                 this.el.hide();
46795                 this.fireEvent("collapsed", this);
46796             },
46797             scope: this,
46798             block: true
46799         });
46800     },
46801
46802     animateExpand : function(){
46803         this.beforeSlide();
46804         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46805         this.el.setStyle("z-index", 20000);
46806         this.collapsedEl.hide({
46807             duration:.1
46808         });
46809         this.el.slideIn(this.getSlideAnchor(), {
46810             callback : function(){
46811                 this.el.setStyle("z-index", "");
46812                 this.afterSlide();
46813                 if(this.split){
46814                     this.split.el.show();
46815                 }
46816                 this.fireEvent("invalidated", this);
46817                 this.fireEvent("expanded", this);
46818             },
46819             scope: this,
46820             block: true
46821         });
46822     },
46823
46824     anchors : {
46825         "west" : "left",
46826         "east" : "right",
46827         "north" : "top",
46828         "south" : "bottom"
46829     },
46830
46831     sanchors : {
46832         "west" : "l",
46833         "east" : "r",
46834         "north" : "t",
46835         "south" : "b"
46836     },
46837
46838     canchors : {
46839         "west" : "tl-tr",
46840         "east" : "tr-tl",
46841         "north" : "tl-bl",
46842         "south" : "bl-tl"
46843     },
46844
46845     getAnchor : function(){
46846         return this.anchors[this.position];
46847     },
46848
46849     getCollapseAnchor : function(){
46850         return this.canchors[this.position];
46851     },
46852
46853     getSlideAnchor : function(){
46854         return this.sanchors[this.position];
46855     },
46856
46857     getAlignAdj : function(){
46858         var cm = this.cmargins;
46859         switch(this.position){
46860             case "west":
46861                 return [0, 0];
46862             break;
46863             case "east":
46864                 return [0, 0];
46865             break;
46866             case "north":
46867                 return [0, 0];
46868             break;
46869             case "south":
46870                 return [0, 0];
46871             break;
46872         }
46873     },
46874
46875     getExpandAdj : function(){
46876         var c = this.collapsedEl, cm = this.cmargins;
46877         switch(this.position){
46878             case "west":
46879                 return [-(cm.right+c.getWidth()+cm.left), 0];
46880             break;
46881             case "east":
46882                 return [cm.right+c.getWidth()+cm.left, 0];
46883             break;
46884             case "north":
46885                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46886             break;
46887             case "south":
46888                 return [0, cm.top+cm.bottom+c.getHeight()];
46889             break;
46890         }
46891     }
46892 });/*
46893  * Based on:
46894  * Ext JS Library 1.1.1
46895  * Copyright(c) 2006-2007, Ext JS, LLC.
46896  *
46897  * Originally Released Under LGPL - original licence link has changed is not relivant.
46898  *
46899  * Fork - LGPL
46900  * <script type="text/javascript">
46901  */
46902 /*
46903  * These classes are private internal classes
46904  */
46905 Roo.CenterLayoutRegion = function(mgr, config){
46906     Roo.LayoutRegion.call(this, mgr, config, "center");
46907     this.visible = true;
46908     this.minWidth = config.minWidth || 20;
46909     this.minHeight = config.minHeight || 20;
46910 };
46911
46912 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46913     hide : function(){
46914         // center panel can't be hidden
46915     },
46916     
46917     show : function(){
46918         // center panel can't be hidden
46919     },
46920     
46921     getMinWidth: function(){
46922         return this.minWidth;
46923     },
46924     
46925     getMinHeight: function(){
46926         return this.minHeight;
46927     }
46928 });
46929
46930
46931 Roo.NorthLayoutRegion = function(mgr, config){
46932     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46933     if(this.split){
46934         this.split.placement = Roo.SplitBar.TOP;
46935         this.split.orientation = Roo.SplitBar.VERTICAL;
46936         this.split.el.addClass("x-layout-split-v");
46937     }
46938     var size = config.initialSize || config.height;
46939     if(typeof size != "undefined"){
46940         this.el.setHeight(size);
46941     }
46942 };
46943 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46944     orientation: Roo.SplitBar.VERTICAL,
46945     getBox : function(){
46946         if(this.collapsed){
46947             return this.collapsedEl.getBox();
46948         }
46949         var box = this.el.getBox();
46950         if(this.split){
46951             box.height += this.split.el.getHeight();
46952         }
46953         return box;
46954     },
46955     
46956     updateBox : function(box){
46957         if(this.split && !this.collapsed){
46958             box.height -= this.split.el.getHeight();
46959             this.split.el.setLeft(box.x);
46960             this.split.el.setTop(box.y+box.height);
46961             this.split.el.setWidth(box.width);
46962         }
46963         if(this.collapsed){
46964             this.updateBody(box.width, null);
46965         }
46966         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46967     }
46968 });
46969
46970 Roo.SouthLayoutRegion = function(mgr, config){
46971     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46972     if(this.split){
46973         this.split.placement = Roo.SplitBar.BOTTOM;
46974         this.split.orientation = Roo.SplitBar.VERTICAL;
46975         this.split.el.addClass("x-layout-split-v");
46976     }
46977     var size = config.initialSize || config.height;
46978     if(typeof size != "undefined"){
46979         this.el.setHeight(size);
46980     }
46981 };
46982 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46983     orientation: Roo.SplitBar.VERTICAL,
46984     getBox : function(){
46985         if(this.collapsed){
46986             return this.collapsedEl.getBox();
46987         }
46988         var box = this.el.getBox();
46989         if(this.split){
46990             var sh = this.split.el.getHeight();
46991             box.height += sh;
46992             box.y -= sh;
46993         }
46994         return box;
46995     },
46996     
46997     updateBox : function(box){
46998         if(this.split && !this.collapsed){
46999             var sh = this.split.el.getHeight();
47000             box.height -= sh;
47001             box.y += sh;
47002             this.split.el.setLeft(box.x);
47003             this.split.el.setTop(box.y-sh);
47004             this.split.el.setWidth(box.width);
47005         }
47006         if(this.collapsed){
47007             this.updateBody(box.width, null);
47008         }
47009         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47010     }
47011 });
47012
47013 Roo.EastLayoutRegion = function(mgr, config){
47014     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
47015     if(this.split){
47016         this.split.placement = Roo.SplitBar.RIGHT;
47017         this.split.orientation = Roo.SplitBar.HORIZONTAL;
47018         this.split.el.addClass("x-layout-split-h");
47019     }
47020     var size = config.initialSize || config.width;
47021     if(typeof size != "undefined"){
47022         this.el.setWidth(size);
47023     }
47024 };
47025 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
47026     orientation: Roo.SplitBar.HORIZONTAL,
47027     getBox : function(){
47028         if(this.collapsed){
47029             return this.collapsedEl.getBox();
47030         }
47031         var box = this.el.getBox();
47032         if(this.split){
47033             var sw = this.split.el.getWidth();
47034             box.width += sw;
47035             box.x -= sw;
47036         }
47037         return box;
47038     },
47039
47040     updateBox : function(box){
47041         if(this.split && !this.collapsed){
47042             var sw = this.split.el.getWidth();
47043             box.width -= sw;
47044             this.split.el.setLeft(box.x);
47045             this.split.el.setTop(box.y);
47046             this.split.el.setHeight(box.height);
47047             box.x += sw;
47048         }
47049         if(this.collapsed){
47050             this.updateBody(null, box.height);
47051         }
47052         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47053     }
47054 });
47055
47056 Roo.WestLayoutRegion = function(mgr, config){
47057     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
47058     if(this.split){
47059         this.split.placement = Roo.SplitBar.LEFT;
47060         this.split.orientation = Roo.SplitBar.HORIZONTAL;
47061         this.split.el.addClass("x-layout-split-h");
47062     }
47063     var size = config.initialSize || config.width;
47064     if(typeof size != "undefined"){
47065         this.el.setWidth(size);
47066     }
47067 };
47068 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
47069     orientation: Roo.SplitBar.HORIZONTAL,
47070     getBox : function(){
47071         if(this.collapsed){
47072             return this.collapsedEl.getBox();
47073         }
47074         var box = this.el.getBox();
47075         if(this.split){
47076             box.width += this.split.el.getWidth();
47077         }
47078         return box;
47079     },
47080     
47081     updateBox : function(box){
47082         if(this.split && !this.collapsed){
47083             var sw = this.split.el.getWidth();
47084             box.width -= sw;
47085             this.split.el.setLeft(box.x+box.width);
47086             this.split.el.setTop(box.y);
47087             this.split.el.setHeight(box.height);
47088         }
47089         if(this.collapsed){
47090             this.updateBody(null, box.height);
47091         }
47092         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47093     }
47094 });
47095 /*
47096  * Based on:
47097  * Ext JS Library 1.1.1
47098  * Copyright(c) 2006-2007, Ext JS, LLC.
47099  *
47100  * Originally Released Under LGPL - original licence link has changed is not relivant.
47101  *
47102  * Fork - LGPL
47103  * <script type="text/javascript">
47104  */
47105  
47106  
47107 /*
47108  * Private internal class for reading and applying state
47109  */
47110 Roo.LayoutStateManager = function(layout){
47111      // default empty state
47112      this.state = {
47113         north: {},
47114         south: {},
47115         east: {},
47116         west: {}       
47117     };
47118 };
47119
47120 Roo.LayoutStateManager.prototype = {
47121     init : function(layout, provider){
47122         this.provider = provider;
47123         var state = provider.get(layout.id+"-layout-state");
47124         if(state){
47125             var wasUpdating = layout.isUpdating();
47126             if(!wasUpdating){
47127                 layout.beginUpdate();
47128             }
47129             for(var key in state){
47130                 if(typeof state[key] != "function"){
47131                     var rstate = state[key];
47132                     var r = layout.getRegion(key);
47133                     if(r && rstate){
47134                         if(rstate.size){
47135                             r.resizeTo(rstate.size);
47136                         }
47137                         if(rstate.collapsed == true){
47138                             r.collapse(true);
47139                         }else{
47140                             r.expand(null, true);
47141                         }
47142                     }
47143                 }
47144             }
47145             if(!wasUpdating){
47146                 layout.endUpdate();
47147             }
47148             this.state = state; 
47149         }
47150         this.layout = layout;
47151         layout.on("regionresized", this.onRegionResized, this);
47152         layout.on("regioncollapsed", this.onRegionCollapsed, this);
47153         layout.on("regionexpanded", this.onRegionExpanded, this);
47154     },
47155     
47156     storeState : function(){
47157         this.provider.set(this.layout.id+"-layout-state", this.state);
47158     },
47159     
47160     onRegionResized : function(region, newSize){
47161         this.state[region.getPosition()].size = newSize;
47162         this.storeState();
47163     },
47164     
47165     onRegionCollapsed : function(region){
47166         this.state[region.getPosition()].collapsed = true;
47167         this.storeState();
47168     },
47169     
47170     onRegionExpanded : function(region){
47171         this.state[region.getPosition()].collapsed = false;
47172         this.storeState();
47173     }
47174 };/*
47175  * Based on:
47176  * Ext JS Library 1.1.1
47177  * Copyright(c) 2006-2007, Ext JS, LLC.
47178  *
47179  * Originally Released Under LGPL - original licence link has changed is not relivant.
47180  *
47181  * Fork - LGPL
47182  * <script type="text/javascript">
47183  */
47184 /**
47185  * @class Roo.ContentPanel
47186  * @extends Roo.util.Observable
47187  * A basic ContentPanel element.
47188  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
47189  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
47190  * @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
47191  * @cfg {Boolean}   closable      True if the panel can be closed/removed
47192  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
47193  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
47194  * @cfg {Toolbar}   toolbar       A toolbar for this panel
47195  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
47196  * @cfg {String} title          The title for this panel
47197  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
47198  * @cfg {String} url            Calls {@link #setUrl} with this value
47199  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
47200  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
47201  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
47202  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
47203
47204  * @constructor
47205  * Create a new ContentPanel.
47206  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
47207  * @param {String/Object} config A string to set only the title or a config object
47208  * @param {String} content (optional) Set the HTML content for this panel
47209  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
47210  */
47211 Roo.ContentPanel = function(el, config, content){
47212     
47213      
47214     /*
47215     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
47216         config = el;
47217         el = Roo.id();
47218     }
47219     if (config && config.parentLayout) { 
47220         el = config.parentLayout.el.createChild(); 
47221     }
47222     */
47223     if(el.autoCreate){ // xtype is available if this is called from factory
47224         config = el;
47225         el = Roo.id();
47226     }
47227     this.el = Roo.get(el);
47228     if(!this.el && config && config.autoCreate){
47229         if(typeof config.autoCreate == "object"){
47230             if(!config.autoCreate.id){
47231                 config.autoCreate.id = config.id||el;
47232             }
47233             this.el = Roo.DomHelper.append(document.body,
47234                         config.autoCreate, true);
47235         }else{
47236             this.el = Roo.DomHelper.append(document.body,
47237                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
47238         }
47239     }
47240     this.closable = false;
47241     this.loaded = false;
47242     this.active = false;
47243     if(typeof config == "string"){
47244         this.title = config;
47245     }else{
47246         Roo.apply(this, config);
47247     }
47248     
47249     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
47250         this.wrapEl = this.el.wrap();
47251         this.toolbar.container = this.el.insertSibling(false, 'before');
47252         this.toolbar = new Roo.Toolbar(this.toolbar);
47253     }
47254     
47255     
47256     
47257     if(this.resizeEl){
47258         this.resizeEl = Roo.get(this.resizeEl, true);
47259     }else{
47260         this.resizeEl = this.el;
47261     }
47262     this.addEvents({
47263         /**
47264          * @event activate
47265          * Fires when this panel is activated. 
47266          * @param {Roo.ContentPanel} this
47267          */
47268         "activate" : true,
47269         /**
47270          * @event deactivate
47271          * Fires when this panel is activated. 
47272          * @param {Roo.ContentPanel} this
47273          */
47274         "deactivate" : true,
47275
47276         /**
47277          * @event resize
47278          * Fires when this panel is resized if fitToFrame is true.
47279          * @param {Roo.ContentPanel} this
47280          * @param {Number} width The width after any component adjustments
47281          * @param {Number} height The height after any component adjustments
47282          */
47283         "resize" : true,
47284         
47285          /**
47286          * @event render
47287          * Fires when this tab is created
47288          * @param {Roo.ContentPanel} this
47289          */
47290         "render" : true
47291         
47292         
47293         
47294     });
47295     if(this.autoScroll){
47296         this.resizeEl.setStyle("overflow", "auto");
47297     } else {
47298         // fix randome scrolling
47299         this.el.on('scroll', function() {
47300             Roo.log('fix random scolling');
47301             this.scrollTo('top',0); 
47302         });
47303     }
47304     content = content || this.content;
47305     if(content){
47306         this.setContent(content);
47307     }
47308     if(config && config.url){
47309         this.setUrl(this.url, this.params, this.loadOnce);
47310     }
47311     
47312     
47313     
47314     Roo.ContentPanel.superclass.constructor.call(this);
47315     
47316     this.fireEvent('render', this);
47317 };
47318
47319 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
47320     tabTip:'',
47321     setRegion : function(region){
47322         this.region = region;
47323         if(region){
47324            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
47325         }else{
47326            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
47327         } 
47328     },
47329     
47330     /**
47331      * Returns the toolbar for this Panel if one was configured. 
47332      * @return {Roo.Toolbar} 
47333      */
47334     getToolbar : function(){
47335         return this.toolbar;
47336     },
47337     
47338     setActiveState : function(active){
47339         this.active = active;
47340         if(!active){
47341             this.fireEvent("deactivate", this);
47342         }else{
47343             this.fireEvent("activate", this);
47344         }
47345     },
47346     /**
47347      * Updates this panel's element
47348      * @param {String} content The new content
47349      * @param {Boolean} loadScripts (optional) true to look for and process scripts
47350     */
47351     setContent : function(content, loadScripts){
47352         this.el.update(content, loadScripts);
47353     },
47354
47355     ignoreResize : function(w, h){
47356         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
47357             return true;
47358         }else{
47359             this.lastSize = {width: w, height: h};
47360             return false;
47361         }
47362     },
47363     /**
47364      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
47365      * @return {Roo.UpdateManager} The UpdateManager
47366      */
47367     getUpdateManager : function(){
47368         return this.el.getUpdateManager();
47369     },
47370      /**
47371      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
47372      * @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:
47373 <pre><code>
47374 panel.load({
47375     url: "your-url.php",
47376     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
47377     callback: yourFunction,
47378     scope: yourObject, //(optional scope)
47379     discardUrl: false,
47380     nocache: false,
47381     text: "Loading...",
47382     timeout: 30,
47383     scripts: false
47384 });
47385 </code></pre>
47386      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
47387      * 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.
47388      * @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}
47389      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
47390      * @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.
47391      * @return {Roo.ContentPanel} this
47392      */
47393     load : function(){
47394         var um = this.el.getUpdateManager();
47395         um.update.apply(um, arguments);
47396         return this;
47397     },
47398
47399
47400     /**
47401      * 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.
47402      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
47403      * @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)
47404      * @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)
47405      * @return {Roo.UpdateManager} The UpdateManager
47406      */
47407     setUrl : function(url, params, loadOnce){
47408         if(this.refreshDelegate){
47409             this.removeListener("activate", this.refreshDelegate);
47410         }
47411         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47412         this.on("activate", this.refreshDelegate);
47413         return this.el.getUpdateManager();
47414     },
47415     
47416     _handleRefresh : function(url, params, loadOnce){
47417         if(!loadOnce || !this.loaded){
47418             var updater = this.el.getUpdateManager();
47419             updater.update(url, params, this._setLoaded.createDelegate(this));
47420         }
47421     },
47422     
47423     _setLoaded : function(){
47424         this.loaded = true;
47425     }, 
47426     
47427     /**
47428      * Returns this panel's id
47429      * @return {String} 
47430      */
47431     getId : function(){
47432         return this.el.id;
47433     },
47434     
47435     /** 
47436      * Returns this panel's element - used by regiosn to add.
47437      * @return {Roo.Element} 
47438      */
47439     getEl : function(){
47440         return this.wrapEl || this.el;
47441     },
47442     
47443     adjustForComponents : function(width, height){
47444         if(this.resizeEl != this.el){
47445             width -= this.el.getFrameWidth('lr');
47446             height -= this.el.getFrameWidth('tb');
47447         }
47448         if(this.toolbar){
47449             var te = this.toolbar.getEl();
47450             height -= te.getHeight();
47451             te.setWidth(width);
47452         }
47453         if(this.adjustments){
47454             width += this.adjustments[0];
47455             height += this.adjustments[1];
47456         }
47457         return {"width": width, "height": height};
47458     },
47459     
47460     setSize : function(width, height){
47461         if(this.fitToFrame && !this.ignoreResize(width, height)){
47462             if(this.fitContainer && this.resizeEl != this.el){
47463                 this.el.setSize(width, height);
47464             }
47465             var size = this.adjustForComponents(width, height);
47466             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
47467             this.fireEvent('resize', this, size.width, size.height);
47468         }
47469     },
47470     
47471     /**
47472      * Returns this panel's title
47473      * @return {String} 
47474      */
47475     getTitle : function(){
47476         return this.title;
47477     },
47478     
47479     /**
47480      * Set this panel's title
47481      * @param {String} title
47482      */
47483     setTitle : function(title){
47484         this.title = title;
47485         if(this.region){
47486             this.region.updatePanelTitle(this, title);
47487         }
47488     },
47489     
47490     /**
47491      * Returns true is this panel was configured to be closable
47492      * @return {Boolean} 
47493      */
47494     isClosable : function(){
47495         return this.closable;
47496     },
47497     
47498     beforeSlide : function(){
47499         this.el.clip();
47500         this.resizeEl.clip();
47501     },
47502     
47503     afterSlide : function(){
47504         this.el.unclip();
47505         this.resizeEl.unclip();
47506     },
47507     
47508     /**
47509      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
47510      *   Will fail silently if the {@link #setUrl} method has not been called.
47511      *   This does not activate the panel, just updates its content.
47512      */
47513     refresh : function(){
47514         if(this.refreshDelegate){
47515            this.loaded = false;
47516            this.refreshDelegate();
47517         }
47518     },
47519     
47520     /**
47521      * Destroys this panel
47522      */
47523     destroy : function(){
47524         this.el.removeAllListeners();
47525         var tempEl = document.createElement("span");
47526         tempEl.appendChild(this.el.dom);
47527         tempEl.innerHTML = "";
47528         this.el.remove();
47529         this.el = null;
47530     },
47531     
47532     /**
47533      * form - if the content panel contains a form - this is a reference to it.
47534      * @type {Roo.form.Form}
47535      */
47536     form : false,
47537     /**
47538      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
47539      *    This contains a reference to it.
47540      * @type {Roo.View}
47541      */
47542     view : false,
47543     
47544       /**
47545      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
47546      * <pre><code>
47547
47548 layout.addxtype({
47549        xtype : 'Form',
47550        items: [ .... ]
47551    }
47552 );
47553
47554 </code></pre>
47555      * @param {Object} cfg Xtype definition of item to add.
47556      */
47557     
47558     addxtype : function(cfg) {
47559         // add form..
47560         if (cfg.xtype.match(/^Form$/)) {
47561             var el = this.el.createChild();
47562
47563             this.form = new  Roo.form.Form(cfg);
47564             
47565             
47566             if ( this.form.allItems.length) this.form.render(el.dom);
47567             return this.form;
47568         }
47569         // should only have one of theses..
47570         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
47571             // views..
47572             cfg.el = this.el.appendChild(document.createElement("div"));
47573             // factory?
47574             
47575             var ret = new Roo.factory(cfg);
47576             ret.render && ret.render(false, ''); // render blank..
47577             this.view = ret;
47578             return ret;
47579         }
47580         return false;
47581     }
47582 });
47583
47584 /**
47585  * @class Roo.GridPanel
47586  * @extends Roo.ContentPanel
47587  * @constructor
47588  * Create a new GridPanel.
47589  * @param {Roo.grid.Grid} grid The grid for this panel
47590  * @param {String/Object} config A string to set only the panel's title, or a config object
47591  */
47592 Roo.GridPanel = function(grid, config){
47593     
47594   
47595     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
47596         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
47597         
47598     this.wrapper.dom.appendChild(grid.getGridEl().dom);
47599     
47600     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
47601     
47602     if(this.toolbar){
47603         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
47604     }
47605     // xtype created footer. - not sure if will work as we normally have to render first..
47606     if (this.footer && !this.footer.el && this.footer.xtype) {
47607         
47608         this.footer.container = this.grid.getView().getFooterPanel(true);
47609         this.footer.dataSource = this.grid.dataSource;
47610         this.footer = Roo.factory(this.footer, Roo);
47611         
47612     }
47613     
47614     grid.monitorWindowResize = false; // turn off autosizing
47615     grid.autoHeight = false;
47616     grid.autoWidth = false;
47617     this.grid = grid;
47618     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47619 };
47620
47621 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47622     getId : function(){
47623         return this.grid.id;
47624     },
47625     
47626     /**
47627      * Returns the grid for this panel
47628      * @return {Roo.grid.Grid} 
47629      */
47630     getGrid : function(){
47631         return this.grid;    
47632     },
47633     
47634     setSize : function(width, height){
47635         if(!this.ignoreResize(width, height)){
47636             var grid = this.grid;
47637             var size = this.adjustForComponents(width, height);
47638             grid.getGridEl().setSize(size.width, size.height);
47639             grid.autoSize();
47640         }
47641     },
47642     
47643     beforeSlide : function(){
47644         this.grid.getView().scroller.clip();
47645     },
47646     
47647     afterSlide : function(){
47648         this.grid.getView().scroller.unclip();
47649     },
47650     
47651     destroy : function(){
47652         this.grid.destroy();
47653         delete this.grid;
47654         Roo.GridPanel.superclass.destroy.call(this); 
47655     }
47656 });
47657
47658
47659 /**
47660  * @class Roo.NestedLayoutPanel
47661  * @extends Roo.ContentPanel
47662  * @constructor
47663  * Create a new NestedLayoutPanel.
47664  * 
47665  * 
47666  * @param {Roo.BorderLayout} layout The layout for this panel
47667  * @param {String/Object} config A string to set only the title or a config object
47668  */
47669 Roo.NestedLayoutPanel = function(layout, config)
47670 {
47671     // construct with only one argument..
47672     /* FIXME - implement nicer consturctors
47673     if (layout.layout) {
47674         config = layout;
47675         layout = config.layout;
47676         delete config.layout;
47677     }
47678     if (layout.xtype && !layout.getEl) {
47679         // then layout needs constructing..
47680         layout = Roo.factory(layout, Roo);
47681     }
47682     */
47683     
47684     
47685     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47686     
47687     layout.monitorWindowResize = false; // turn off autosizing
47688     this.layout = layout;
47689     this.layout.getEl().addClass("x-layout-nested-layout");
47690     
47691     
47692     
47693     
47694 };
47695
47696 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47697
47698     setSize : function(width, height){
47699         if(!this.ignoreResize(width, height)){
47700             var size = this.adjustForComponents(width, height);
47701             var el = this.layout.getEl();
47702             el.setSize(size.width, size.height);
47703             var touch = el.dom.offsetWidth;
47704             this.layout.layout();
47705             // ie requires a double layout on the first pass
47706             if(Roo.isIE && !this.initialized){
47707                 this.initialized = true;
47708                 this.layout.layout();
47709             }
47710         }
47711     },
47712     
47713     // activate all subpanels if not currently active..
47714     
47715     setActiveState : function(active){
47716         this.active = active;
47717         if(!active){
47718             this.fireEvent("deactivate", this);
47719             return;
47720         }
47721         
47722         this.fireEvent("activate", this);
47723         // not sure if this should happen before or after..
47724         if (!this.layout) {
47725             return; // should not happen..
47726         }
47727         var reg = false;
47728         for (var r in this.layout.regions) {
47729             reg = this.layout.getRegion(r);
47730             if (reg.getActivePanel()) {
47731                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47732                 reg.setActivePanel(reg.getActivePanel());
47733                 continue;
47734             }
47735             if (!reg.panels.length) {
47736                 continue;
47737             }
47738             reg.showPanel(reg.getPanel(0));
47739         }
47740         
47741         
47742         
47743         
47744     },
47745     
47746     /**
47747      * Returns the nested BorderLayout for this panel
47748      * @return {Roo.BorderLayout} 
47749      */
47750     getLayout : function(){
47751         return this.layout;
47752     },
47753     
47754      /**
47755      * Adds a xtype elements to the layout of the nested panel
47756      * <pre><code>
47757
47758 panel.addxtype({
47759        xtype : 'ContentPanel',
47760        region: 'west',
47761        items: [ .... ]
47762    }
47763 );
47764
47765 panel.addxtype({
47766         xtype : 'NestedLayoutPanel',
47767         region: 'west',
47768         layout: {
47769            center: { },
47770            west: { }   
47771         },
47772         items : [ ... list of content panels or nested layout panels.. ]
47773    }
47774 );
47775 </code></pre>
47776      * @param {Object} cfg Xtype definition of item to add.
47777      */
47778     addxtype : function(cfg) {
47779         return this.layout.addxtype(cfg);
47780     
47781     }
47782 });
47783
47784 Roo.ScrollPanel = function(el, config, content){
47785     config = config || {};
47786     config.fitToFrame = true;
47787     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47788     
47789     this.el.dom.style.overflow = "hidden";
47790     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47791     this.el.removeClass("x-layout-inactive-content");
47792     this.el.on("mousewheel", this.onWheel, this);
47793
47794     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47795     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47796     up.unselectable(); down.unselectable();
47797     up.on("click", this.scrollUp, this);
47798     down.on("click", this.scrollDown, this);
47799     up.addClassOnOver("x-scroller-btn-over");
47800     down.addClassOnOver("x-scroller-btn-over");
47801     up.addClassOnClick("x-scroller-btn-click");
47802     down.addClassOnClick("x-scroller-btn-click");
47803     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47804
47805     this.resizeEl = this.el;
47806     this.el = wrap; this.up = up; this.down = down;
47807 };
47808
47809 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47810     increment : 100,
47811     wheelIncrement : 5,
47812     scrollUp : function(){
47813         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47814     },
47815
47816     scrollDown : function(){
47817         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47818     },
47819
47820     afterScroll : function(){
47821         var el = this.resizeEl;
47822         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47823         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47824         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47825     },
47826
47827     setSize : function(){
47828         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47829         this.afterScroll();
47830     },
47831
47832     onWheel : function(e){
47833         var d = e.getWheelDelta();
47834         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47835         this.afterScroll();
47836         e.stopEvent();
47837     },
47838
47839     setContent : function(content, loadScripts){
47840         this.resizeEl.update(content, loadScripts);
47841     }
47842
47843 });
47844
47845
47846
47847
47848
47849
47850
47851
47852
47853 /**
47854  * @class Roo.TreePanel
47855  * @extends Roo.ContentPanel
47856  * @constructor
47857  * Create a new TreePanel. - defaults to fit/scoll contents.
47858  * @param {String/Object} config A string to set only the panel's title, or a config object
47859  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47860  */
47861 Roo.TreePanel = function(config){
47862     var el = config.el;
47863     var tree = config.tree;
47864     delete config.tree; 
47865     delete config.el; // hopefull!
47866     
47867     // wrapper for IE7 strict & safari scroll issue
47868     
47869     var treeEl = el.createChild();
47870     config.resizeEl = treeEl;
47871     
47872     
47873     
47874     Roo.TreePanel.superclass.constructor.call(this, el, config);
47875  
47876  
47877     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47878     //console.log(tree);
47879     this.on('activate', function()
47880     {
47881         if (this.tree.rendered) {
47882             return;
47883         }
47884         //console.log('render tree');
47885         this.tree.render();
47886     });
47887     
47888     this.on('resize',  function (cp, w, h) {
47889             this.tree.innerCt.setWidth(w);
47890             this.tree.innerCt.setHeight(h);
47891             this.tree.innerCt.setStyle('overflow-y', 'auto');
47892     });
47893
47894         
47895     
47896 };
47897
47898 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47899     fitToFrame : true,
47900     autoScroll : true
47901 });
47902
47903
47904
47905
47906
47907
47908
47909
47910
47911
47912
47913 /*
47914  * Based on:
47915  * Ext JS Library 1.1.1
47916  * Copyright(c) 2006-2007, Ext JS, LLC.
47917  *
47918  * Originally Released Under LGPL - original licence link has changed is not relivant.
47919  *
47920  * Fork - LGPL
47921  * <script type="text/javascript">
47922  */
47923  
47924
47925 /**
47926  * @class Roo.ReaderLayout
47927  * @extends Roo.BorderLayout
47928  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47929  * center region containing two nested regions (a top one for a list view and one for item preview below),
47930  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47931  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47932  * expedites the setup of the overall layout and regions for this common application style.
47933  * Example:
47934  <pre><code>
47935 var reader = new Roo.ReaderLayout();
47936 var CP = Roo.ContentPanel;  // shortcut for adding
47937
47938 reader.beginUpdate();
47939 reader.add("north", new CP("north", "North"));
47940 reader.add("west", new CP("west", {title: "West"}));
47941 reader.add("east", new CP("east", {title: "East"}));
47942
47943 reader.regions.listView.add(new CP("listView", "List"));
47944 reader.regions.preview.add(new CP("preview", "Preview"));
47945 reader.endUpdate();
47946 </code></pre>
47947 * @constructor
47948 * Create a new ReaderLayout
47949 * @param {Object} config Configuration options
47950 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47951 * document.body if omitted)
47952 */
47953 Roo.ReaderLayout = function(config, renderTo){
47954     var c = config || {size:{}};
47955     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47956         north: c.north !== false ? Roo.apply({
47957             split:false,
47958             initialSize: 32,
47959             titlebar: false
47960         }, c.north) : false,
47961         west: c.west !== false ? Roo.apply({
47962             split:true,
47963             initialSize: 200,
47964             minSize: 175,
47965             maxSize: 400,
47966             titlebar: true,
47967             collapsible: true,
47968             animate: true,
47969             margins:{left:5,right:0,bottom:5,top:5},
47970             cmargins:{left:5,right:5,bottom:5,top:5}
47971         }, c.west) : false,
47972         east: c.east !== false ? Roo.apply({
47973             split:true,
47974             initialSize: 200,
47975             minSize: 175,
47976             maxSize: 400,
47977             titlebar: true,
47978             collapsible: true,
47979             animate: true,
47980             margins:{left:0,right:5,bottom:5,top:5},
47981             cmargins:{left:5,right:5,bottom:5,top:5}
47982         }, c.east) : false,
47983         center: Roo.apply({
47984             tabPosition: 'top',
47985             autoScroll:false,
47986             closeOnTab: true,
47987             titlebar:false,
47988             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47989         }, c.center)
47990     });
47991
47992     this.el.addClass('x-reader');
47993
47994     this.beginUpdate();
47995
47996     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47997         south: c.preview !== false ? Roo.apply({
47998             split:true,
47999             initialSize: 200,
48000             minSize: 100,
48001             autoScroll:true,
48002             collapsible:true,
48003             titlebar: true,
48004             cmargins:{top:5,left:0, right:0, bottom:0}
48005         }, c.preview) : false,
48006         center: Roo.apply({
48007             autoScroll:false,
48008             titlebar:false,
48009             minHeight:200
48010         }, c.listView)
48011     });
48012     this.add('center', new Roo.NestedLayoutPanel(inner,
48013             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
48014
48015     this.endUpdate();
48016
48017     this.regions.preview = inner.getRegion('south');
48018     this.regions.listView = inner.getRegion('center');
48019 };
48020
48021 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
48022  * Based on:
48023  * Ext JS Library 1.1.1
48024  * Copyright(c) 2006-2007, Ext JS, LLC.
48025  *
48026  * Originally Released Under LGPL - original licence link has changed is not relivant.
48027  *
48028  * Fork - LGPL
48029  * <script type="text/javascript">
48030  */
48031  
48032 /**
48033  * @class Roo.grid.Grid
48034  * @extends Roo.util.Observable
48035  * This class represents the primary interface of a component based grid control.
48036  * <br><br>Usage:<pre><code>
48037  var grid = new Roo.grid.Grid("my-container-id", {
48038      ds: myDataStore,
48039      cm: myColModel,
48040      selModel: mySelectionModel,
48041      autoSizeColumns: true,
48042      monitorWindowResize: false,
48043      trackMouseOver: true
48044  });
48045  // set any options
48046  grid.render();
48047  * </code></pre>
48048  * <b>Common Problems:</b><br/>
48049  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
48050  * element will correct this<br/>
48051  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
48052  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
48053  * are unpredictable.<br/>
48054  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
48055  * grid to calculate dimensions/offsets.<br/>
48056   * @constructor
48057  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
48058  * The container MUST have some type of size defined for the grid to fill. The container will be
48059  * automatically set to position relative if it isn't already.
48060  * @param {Object} config A config object that sets properties on this grid.
48061  */
48062 Roo.grid.Grid = function(container, config){
48063         // initialize the container
48064         this.container = Roo.get(container);
48065         this.container.update("");
48066         this.container.setStyle("overflow", "hidden");
48067     this.container.addClass('x-grid-container');
48068
48069     this.id = this.container.id;
48070
48071     Roo.apply(this, config);
48072     // check and correct shorthanded configs
48073     if(this.ds){
48074         this.dataSource = this.ds;
48075         delete this.ds;
48076     }
48077     if(this.cm){
48078         this.colModel = this.cm;
48079         delete this.cm;
48080     }
48081     if(this.sm){
48082         this.selModel = this.sm;
48083         delete this.sm;
48084     }
48085
48086     if (this.selModel) {
48087         this.selModel = Roo.factory(this.selModel, Roo.grid);
48088         this.sm = this.selModel;
48089         this.sm.xmodule = this.xmodule || false;
48090     }
48091     if (typeof(this.colModel.config) == 'undefined') {
48092         this.colModel = new Roo.grid.ColumnModel(this.colModel);
48093         this.cm = this.colModel;
48094         this.cm.xmodule = this.xmodule || false;
48095     }
48096     if (this.dataSource) {
48097         this.dataSource= Roo.factory(this.dataSource, Roo.data);
48098         this.ds = this.dataSource;
48099         this.ds.xmodule = this.xmodule || false;
48100          
48101     }
48102     
48103     
48104     
48105     if(this.width){
48106         this.container.setWidth(this.width);
48107     }
48108
48109     if(this.height){
48110         this.container.setHeight(this.height);
48111     }
48112     /** @private */
48113         this.addEvents({
48114         // raw events
48115         /**
48116          * @event click
48117          * The raw click event for the entire grid.
48118          * @param {Roo.EventObject} e
48119          */
48120         "click" : true,
48121         /**
48122          * @event dblclick
48123          * The raw dblclick event for the entire grid.
48124          * @param {Roo.EventObject} e
48125          */
48126         "dblclick" : true,
48127         /**
48128          * @event contextmenu
48129          * The raw contextmenu event for the entire grid.
48130          * @param {Roo.EventObject} e
48131          */
48132         "contextmenu" : true,
48133         /**
48134          * @event mousedown
48135          * The raw mousedown event for the entire grid.
48136          * @param {Roo.EventObject} e
48137          */
48138         "mousedown" : true,
48139         /**
48140          * @event mouseup
48141          * The raw mouseup event for the entire grid.
48142          * @param {Roo.EventObject} e
48143          */
48144         "mouseup" : true,
48145         /**
48146          * @event mouseover
48147          * The raw mouseover event for the entire grid.
48148          * @param {Roo.EventObject} e
48149          */
48150         "mouseover" : true,
48151         /**
48152          * @event mouseout
48153          * The raw mouseout event for the entire grid.
48154          * @param {Roo.EventObject} e
48155          */
48156         "mouseout" : true,
48157         /**
48158          * @event keypress
48159          * The raw keypress event for the entire grid.
48160          * @param {Roo.EventObject} e
48161          */
48162         "keypress" : true,
48163         /**
48164          * @event keydown
48165          * The raw keydown event for the entire grid.
48166          * @param {Roo.EventObject} e
48167          */
48168         "keydown" : true,
48169
48170         // custom events
48171
48172         /**
48173          * @event cellclick
48174          * Fires when a cell is clicked
48175          * @param {Grid} this
48176          * @param {Number} rowIndex
48177          * @param {Number} columnIndex
48178          * @param {Roo.EventObject} e
48179          */
48180         "cellclick" : true,
48181         /**
48182          * @event celldblclick
48183          * Fires when a cell is double clicked
48184          * @param {Grid} this
48185          * @param {Number} rowIndex
48186          * @param {Number} columnIndex
48187          * @param {Roo.EventObject} e
48188          */
48189         "celldblclick" : true,
48190         /**
48191          * @event rowclick
48192          * Fires when a row is clicked
48193          * @param {Grid} this
48194          * @param {Number} rowIndex
48195          * @param {Roo.EventObject} e
48196          */
48197         "rowclick" : true,
48198         /**
48199          * @event rowdblclick
48200          * Fires when a row is double clicked
48201          * @param {Grid} this
48202          * @param {Number} rowIndex
48203          * @param {Roo.EventObject} e
48204          */
48205         "rowdblclick" : true,
48206         /**
48207          * @event headerclick
48208          * Fires when a header is clicked
48209          * @param {Grid} this
48210          * @param {Number} columnIndex
48211          * @param {Roo.EventObject} e
48212          */
48213         "headerclick" : true,
48214         /**
48215          * @event headerdblclick
48216          * Fires when a header cell is double clicked
48217          * @param {Grid} this
48218          * @param {Number} columnIndex
48219          * @param {Roo.EventObject} e
48220          */
48221         "headerdblclick" : true,
48222         /**
48223          * @event rowcontextmenu
48224          * Fires when a row is right clicked
48225          * @param {Grid} this
48226          * @param {Number} rowIndex
48227          * @param {Roo.EventObject} e
48228          */
48229         "rowcontextmenu" : true,
48230         /**
48231          * @event cellcontextmenu
48232          * Fires when a cell is right clicked
48233          * @param {Grid} this
48234          * @param {Number} rowIndex
48235          * @param {Number} cellIndex
48236          * @param {Roo.EventObject} e
48237          */
48238          "cellcontextmenu" : true,
48239         /**
48240          * @event headercontextmenu
48241          * Fires when a header is right clicked
48242          * @param {Grid} this
48243          * @param {Number} columnIndex
48244          * @param {Roo.EventObject} e
48245          */
48246         "headercontextmenu" : true,
48247         /**
48248          * @event bodyscroll
48249          * Fires when the body element is scrolled
48250          * @param {Number} scrollLeft
48251          * @param {Number} scrollTop
48252          */
48253         "bodyscroll" : true,
48254         /**
48255          * @event columnresize
48256          * Fires when the user resizes a column
48257          * @param {Number} columnIndex
48258          * @param {Number} newSize
48259          */
48260         "columnresize" : true,
48261         /**
48262          * @event columnmove
48263          * Fires when the user moves a column
48264          * @param {Number} oldIndex
48265          * @param {Number} newIndex
48266          */
48267         "columnmove" : true,
48268         /**
48269          * @event startdrag
48270          * Fires when row(s) start being dragged
48271          * @param {Grid} this
48272          * @param {Roo.GridDD} dd The drag drop object
48273          * @param {event} e The raw browser event
48274          */
48275         "startdrag" : true,
48276         /**
48277          * @event enddrag
48278          * Fires when a drag operation is complete
48279          * @param {Grid} this
48280          * @param {Roo.GridDD} dd The drag drop object
48281          * @param {event} e The raw browser event
48282          */
48283         "enddrag" : true,
48284         /**
48285          * @event dragdrop
48286          * Fires when dragged row(s) are dropped on a valid DD target
48287          * @param {Grid} this
48288          * @param {Roo.GridDD} dd The drag drop object
48289          * @param {String} targetId The target drag drop object
48290          * @param {event} e The raw browser event
48291          */
48292         "dragdrop" : true,
48293         /**
48294          * @event dragover
48295          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
48296          * @param {Grid} this
48297          * @param {Roo.GridDD} dd The drag drop object
48298          * @param {String} targetId The target drag drop object
48299          * @param {event} e The raw browser event
48300          */
48301         "dragover" : true,
48302         /**
48303          * @event dragenter
48304          *  Fires when the dragged row(s) first cross another DD target while being dragged
48305          * @param {Grid} this
48306          * @param {Roo.GridDD} dd The drag drop object
48307          * @param {String} targetId The target drag drop object
48308          * @param {event} e The raw browser event
48309          */
48310         "dragenter" : true,
48311         /**
48312          * @event dragout
48313          * Fires when the dragged row(s) leave another DD target while being dragged
48314          * @param {Grid} this
48315          * @param {Roo.GridDD} dd The drag drop object
48316          * @param {String} targetId The target drag drop object
48317          * @param {event} e The raw browser event
48318          */
48319         "dragout" : true,
48320         /**
48321          * @event rowclass
48322          * Fires when a row is rendered, so you can change add a style to it.
48323          * @param {GridView} gridview   The grid view
48324          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
48325          */
48326         'rowclass' : true,
48327
48328         /**
48329          * @event render
48330          * Fires when the grid is rendered
48331          * @param {Grid} grid
48332          */
48333         'render' : true
48334     });
48335
48336     Roo.grid.Grid.superclass.constructor.call(this);
48337 };
48338 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
48339     
48340     /**
48341      * @cfg {String} ddGroup - drag drop group.
48342      */
48343
48344     /**
48345      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
48346      */
48347     minColumnWidth : 25,
48348
48349     /**
48350      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
48351      * <b>on initial render.</b> It is more efficient to explicitly size the columns
48352      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
48353      */
48354     autoSizeColumns : false,
48355
48356     /**
48357      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
48358      */
48359     autoSizeHeaders : true,
48360
48361     /**
48362      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
48363      */
48364     monitorWindowResize : true,
48365
48366     /**
48367      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
48368      * rows measured to get a columns size. Default is 0 (all rows).
48369      */
48370     maxRowsToMeasure : 0,
48371
48372     /**
48373      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
48374      */
48375     trackMouseOver : true,
48376
48377     /**
48378     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
48379     */
48380     
48381     /**
48382     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
48383     */
48384     enableDragDrop : false,
48385     
48386     /**
48387     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
48388     */
48389     enableColumnMove : true,
48390     
48391     /**
48392     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
48393     */
48394     enableColumnHide : true,
48395     
48396     /**
48397     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
48398     */
48399     enableRowHeightSync : false,
48400     
48401     /**
48402     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
48403     */
48404     stripeRows : true,
48405     
48406     /**
48407     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
48408     */
48409     autoHeight : false,
48410
48411     /**
48412      * @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.
48413      */
48414     autoExpandColumn : false,
48415
48416     /**
48417     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
48418     * Default is 50.
48419     */
48420     autoExpandMin : 50,
48421
48422     /**
48423     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
48424     */
48425     autoExpandMax : 1000,
48426
48427     /**
48428     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
48429     */
48430     view : null,
48431
48432     /**
48433     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
48434     */
48435     loadMask : false,
48436     /**
48437     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
48438     */
48439     dropTarget: false,
48440     
48441    
48442     
48443     // private
48444     rendered : false,
48445
48446     /**
48447     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
48448     * of a fixed width. Default is false.
48449     */
48450     /**
48451     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
48452     */
48453     /**
48454      * Called once after all setup has been completed and the grid is ready to be rendered.
48455      * @return {Roo.grid.Grid} this
48456      */
48457     render : function()
48458     {
48459         var c = this.container;
48460         // try to detect autoHeight/width mode
48461         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
48462             this.autoHeight = true;
48463         }
48464         var view = this.getView();
48465         view.init(this);
48466
48467         c.on("click", this.onClick, this);
48468         c.on("dblclick", this.onDblClick, this);
48469         c.on("contextmenu", this.onContextMenu, this);
48470         c.on("keydown", this.onKeyDown, this);
48471
48472         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
48473
48474         this.getSelectionModel().init(this);
48475
48476         view.render();
48477
48478         if(this.loadMask){
48479             this.loadMask = new Roo.LoadMask(this.container,
48480                     Roo.apply({store:this.dataSource}, this.loadMask));
48481         }
48482         
48483         
48484         if (this.toolbar && this.toolbar.xtype) {
48485             this.toolbar.container = this.getView().getHeaderPanel(true);
48486             this.toolbar = new Roo.Toolbar(this.toolbar);
48487         }
48488         if (this.footer && this.footer.xtype) {
48489             this.footer.dataSource = this.getDataSource();
48490             this.footer.container = this.getView().getFooterPanel(true);
48491             this.footer = Roo.factory(this.footer, Roo);
48492         }
48493         if (this.dropTarget && this.dropTarget.xtype) {
48494             delete this.dropTarget.xtype;
48495             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
48496         }
48497         
48498         
48499         this.rendered = true;
48500         this.fireEvent('render', this);
48501         return this;
48502     },
48503
48504         /**
48505          * Reconfigures the grid to use a different Store and Column Model.
48506          * The View will be bound to the new objects and refreshed.
48507          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
48508          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
48509          */
48510     reconfigure : function(dataSource, colModel){
48511         if(this.loadMask){
48512             this.loadMask.destroy();
48513             this.loadMask = new Roo.LoadMask(this.container,
48514                     Roo.apply({store:dataSource}, this.loadMask));
48515         }
48516         this.view.bind(dataSource, colModel);
48517         this.dataSource = dataSource;
48518         this.colModel = colModel;
48519         this.view.refresh(true);
48520     },
48521
48522     // private
48523     onKeyDown : function(e){
48524         this.fireEvent("keydown", e);
48525     },
48526
48527     /**
48528      * Destroy this grid.
48529      * @param {Boolean} removeEl True to remove the element
48530      */
48531     destroy : function(removeEl, keepListeners){
48532         if(this.loadMask){
48533             this.loadMask.destroy();
48534         }
48535         var c = this.container;
48536         c.removeAllListeners();
48537         this.view.destroy();
48538         this.colModel.purgeListeners();
48539         if(!keepListeners){
48540             this.purgeListeners();
48541         }
48542         c.update("");
48543         if(removeEl === true){
48544             c.remove();
48545         }
48546     },
48547
48548     // private
48549     processEvent : function(name, e){
48550         this.fireEvent(name, e);
48551         var t = e.getTarget();
48552         var v = this.view;
48553         var header = v.findHeaderIndex(t);
48554         if(header !== false){
48555             this.fireEvent("header" + name, this, header, e);
48556         }else{
48557             var row = v.findRowIndex(t);
48558             var cell = v.findCellIndex(t);
48559             if(row !== false){
48560                 this.fireEvent("row" + name, this, row, e);
48561                 if(cell !== false){
48562                     this.fireEvent("cell" + name, this, row, cell, e);
48563                 }
48564             }
48565         }
48566     },
48567
48568     // private
48569     onClick : function(e){
48570         this.processEvent("click", e);
48571     },
48572
48573     // private
48574     onContextMenu : function(e, t){
48575         this.processEvent("contextmenu", e);
48576     },
48577
48578     // private
48579     onDblClick : function(e){
48580         this.processEvent("dblclick", e);
48581     },
48582
48583     // private
48584     walkCells : function(row, col, step, fn, scope){
48585         var cm = this.colModel, clen = cm.getColumnCount();
48586         var ds = this.dataSource, rlen = ds.getCount(), first = true;
48587         if(step < 0){
48588             if(col < 0){
48589                 row--;
48590                 first = false;
48591             }
48592             while(row >= 0){
48593                 if(!first){
48594                     col = clen-1;
48595                 }
48596                 first = false;
48597                 while(col >= 0){
48598                     if(fn.call(scope || this, row, col, cm) === true){
48599                         return [row, col];
48600                     }
48601                     col--;
48602                 }
48603                 row--;
48604             }
48605         } else {
48606             if(col >= clen){
48607                 row++;
48608                 first = false;
48609             }
48610             while(row < rlen){
48611                 if(!first){
48612                     col = 0;
48613                 }
48614                 first = false;
48615                 while(col < clen){
48616                     if(fn.call(scope || this, row, col, cm) === true){
48617                         return [row, col];
48618                     }
48619                     col++;
48620                 }
48621                 row++;
48622             }
48623         }
48624         return null;
48625     },
48626
48627     // private
48628     getSelections : function(){
48629         return this.selModel.getSelections();
48630     },
48631
48632     /**
48633      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48634      * but if manual update is required this method will initiate it.
48635      */
48636     autoSize : function(){
48637         if(this.rendered){
48638             this.view.layout();
48639             if(this.view.adjustForScroll){
48640                 this.view.adjustForScroll();
48641             }
48642         }
48643     },
48644
48645     /**
48646      * Returns the grid's underlying element.
48647      * @return {Element} The element
48648      */
48649     getGridEl : function(){
48650         return this.container;
48651     },
48652
48653     // private for compatibility, overridden by editor grid
48654     stopEditing : function(){},
48655
48656     /**
48657      * Returns the grid's SelectionModel.
48658      * @return {SelectionModel}
48659      */
48660     getSelectionModel : function(){
48661         if(!this.selModel){
48662             this.selModel = new Roo.grid.RowSelectionModel();
48663         }
48664         return this.selModel;
48665     },
48666
48667     /**
48668      * Returns the grid's DataSource.
48669      * @return {DataSource}
48670      */
48671     getDataSource : function(){
48672         return this.dataSource;
48673     },
48674
48675     /**
48676      * Returns the grid's ColumnModel.
48677      * @return {ColumnModel}
48678      */
48679     getColumnModel : function(){
48680         return this.colModel;
48681     },
48682
48683     /**
48684      * Returns the grid's GridView object.
48685      * @return {GridView}
48686      */
48687     getView : function(){
48688         if(!this.view){
48689             this.view = new Roo.grid.GridView(this.viewConfig);
48690         }
48691         return this.view;
48692     },
48693     /**
48694      * Called to get grid's drag proxy text, by default returns this.ddText.
48695      * @return {String}
48696      */
48697     getDragDropText : function(){
48698         var count = this.selModel.getCount();
48699         return String.format(this.ddText, count, count == 1 ? '' : 's');
48700     }
48701 });
48702 /**
48703  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48704  * %0 is replaced with the number of selected rows.
48705  * @type String
48706  */
48707 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48708  * Based on:
48709  * Ext JS Library 1.1.1
48710  * Copyright(c) 2006-2007, Ext JS, LLC.
48711  *
48712  * Originally Released Under LGPL - original licence link has changed is not relivant.
48713  *
48714  * Fork - LGPL
48715  * <script type="text/javascript">
48716  */
48717  
48718 Roo.grid.AbstractGridView = function(){
48719         this.grid = null;
48720         
48721         this.events = {
48722             "beforerowremoved" : true,
48723             "beforerowsinserted" : true,
48724             "beforerefresh" : true,
48725             "rowremoved" : true,
48726             "rowsinserted" : true,
48727             "rowupdated" : true,
48728             "refresh" : true
48729         };
48730     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48731 };
48732
48733 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48734     rowClass : "x-grid-row",
48735     cellClass : "x-grid-cell",
48736     tdClass : "x-grid-td",
48737     hdClass : "x-grid-hd",
48738     splitClass : "x-grid-hd-split",
48739     
48740         init: function(grid){
48741         this.grid = grid;
48742                 var cid = this.grid.getGridEl().id;
48743         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48744         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48745         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48746         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48747         },
48748         
48749         getColumnRenderers : function(){
48750         var renderers = [];
48751         var cm = this.grid.colModel;
48752         var colCount = cm.getColumnCount();
48753         for(var i = 0; i < colCount; i++){
48754             renderers[i] = cm.getRenderer(i);
48755         }
48756         return renderers;
48757     },
48758     
48759     getColumnIds : function(){
48760         var ids = [];
48761         var cm = this.grid.colModel;
48762         var colCount = cm.getColumnCount();
48763         for(var i = 0; i < colCount; i++){
48764             ids[i] = cm.getColumnId(i);
48765         }
48766         return ids;
48767     },
48768     
48769     getDataIndexes : function(){
48770         if(!this.indexMap){
48771             this.indexMap = this.buildIndexMap();
48772         }
48773         return this.indexMap.colToData;
48774     },
48775     
48776     getColumnIndexByDataIndex : function(dataIndex){
48777         if(!this.indexMap){
48778             this.indexMap = this.buildIndexMap();
48779         }
48780         return this.indexMap.dataToCol[dataIndex];
48781     },
48782     
48783     /**
48784      * Set a css style for a column dynamically. 
48785      * @param {Number} colIndex The index of the column
48786      * @param {String} name The css property name
48787      * @param {String} value The css value
48788      */
48789     setCSSStyle : function(colIndex, name, value){
48790         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48791         Roo.util.CSS.updateRule(selector, name, value);
48792     },
48793     
48794     generateRules : function(cm){
48795         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48796         Roo.util.CSS.removeStyleSheet(rulesId);
48797         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48798             var cid = cm.getColumnId(i);
48799             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48800                          this.tdSelector, cid, " {\n}\n",
48801                          this.hdSelector, cid, " {\n}\n",
48802                          this.splitSelector, cid, " {\n}\n");
48803         }
48804         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48805     }
48806 });/*
48807  * Based on:
48808  * Ext JS Library 1.1.1
48809  * Copyright(c) 2006-2007, Ext JS, LLC.
48810  *
48811  * Originally Released Under LGPL - original licence link has changed is not relivant.
48812  *
48813  * Fork - LGPL
48814  * <script type="text/javascript">
48815  */
48816
48817 // private
48818 // This is a support class used internally by the Grid components
48819 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48820     this.grid = grid;
48821     this.view = grid.getView();
48822     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48823     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48824     if(hd2){
48825         this.setHandleElId(Roo.id(hd));
48826         this.setOuterHandleElId(Roo.id(hd2));
48827     }
48828     this.scroll = false;
48829 };
48830 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48831     maxDragWidth: 120,
48832     getDragData : function(e){
48833         var t = Roo.lib.Event.getTarget(e);
48834         var h = this.view.findHeaderCell(t);
48835         if(h){
48836             return {ddel: h.firstChild, header:h};
48837         }
48838         return false;
48839     },
48840
48841     onInitDrag : function(e){
48842         this.view.headersDisabled = true;
48843         var clone = this.dragData.ddel.cloneNode(true);
48844         clone.id = Roo.id();
48845         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48846         this.proxy.update(clone);
48847         return true;
48848     },
48849
48850     afterValidDrop : function(){
48851         var v = this.view;
48852         setTimeout(function(){
48853             v.headersDisabled = false;
48854         }, 50);
48855     },
48856
48857     afterInvalidDrop : function(){
48858         var v = this.view;
48859         setTimeout(function(){
48860             v.headersDisabled = false;
48861         }, 50);
48862     }
48863 });
48864 /*
48865  * Based on:
48866  * Ext JS Library 1.1.1
48867  * Copyright(c) 2006-2007, Ext JS, LLC.
48868  *
48869  * Originally Released Under LGPL - original licence link has changed is not relivant.
48870  *
48871  * Fork - LGPL
48872  * <script type="text/javascript">
48873  */
48874 // private
48875 // This is a support class used internally by the Grid components
48876 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48877     this.grid = grid;
48878     this.view = grid.getView();
48879     // split the proxies so they don't interfere with mouse events
48880     this.proxyTop = Roo.DomHelper.append(document.body, {
48881         cls:"col-move-top", html:"&#160;"
48882     }, true);
48883     this.proxyBottom = Roo.DomHelper.append(document.body, {
48884         cls:"col-move-bottom", html:"&#160;"
48885     }, true);
48886     this.proxyTop.hide = this.proxyBottom.hide = function(){
48887         this.setLeftTop(-100,-100);
48888         this.setStyle("visibility", "hidden");
48889     };
48890     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48891     // temporarily disabled
48892     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48893     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48894 };
48895 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48896     proxyOffsets : [-4, -9],
48897     fly: Roo.Element.fly,
48898
48899     getTargetFromEvent : function(e){
48900         var t = Roo.lib.Event.getTarget(e);
48901         var cindex = this.view.findCellIndex(t);
48902         if(cindex !== false){
48903             return this.view.getHeaderCell(cindex);
48904         }
48905         return null;
48906     },
48907
48908     nextVisible : function(h){
48909         var v = this.view, cm = this.grid.colModel;
48910         h = h.nextSibling;
48911         while(h){
48912             if(!cm.isHidden(v.getCellIndex(h))){
48913                 return h;
48914             }
48915             h = h.nextSibling;
48916         }
48917         return null;
48918     },
48919
48920     prevVisible : function(h){
48921         var v = this.view, cm = this.grid.colModel;
48922         h = h.prevSibling;
48923         while(h){
48924             if(!cm.isHidden(v.getCellIndex(h))){
48925                 return h;
48926             }
48927             h = h.prevSibling;
48928         }
48929         return null;
48930     },
48931
48932     positionIndicator : function(h, n, e){
48933         var x = Roo.lib.Event.getPageX(e);
48934         var r = Roo.lib.Dom.getRegion(n.firstChild);
48935         var px, pt, py = r.top + this.proxyOffsets[1];
48936         if((r.right - x) <= (r.right-r.left)/2){
48937             px = r.right+this.view.borderWidth;
48938             pt = "after";
48939         }else{
48940             px = r.left;
48941             pt = "before";
48942         }
48943         var oldIndex = this.view.getCellIndex(h);
48944         var newIndex = this.view.getCellIndex(n);
48945
48946         if(this.grid.colModel.isFixed(newIndex)){
48947             return false;
48948         }
48949
48950         var locked = this.grid.colModel.isLocked(newIndex);
48951
48952         if(pt == "after"){
48953             newIndex++;
48954         }
48955         if(oldIndex < newIndex){
48956             newIndex--;
48957         }
48958         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48959             return false;
48960         }
48961         px +=  this.proxyOffsets[0];
48962         this.proxyTop.setLeftTop(px, py);
48963         this.proxyTop.show();
48964         if(!this.bottomOffset){
48965             this.bottomOffset = this.view.mainHd.getHeight();
48966         }
48967         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48968         this.proxyBottom.show();
48969         return pt;
48970     },
48971
48972     onNodeEnter : function(n, dd, e, data){
48973         if(data.header != n){
48974             this.positionIndicator(data.header, n, e);
48975         }
48976     },
48977
48978     onNodeOver : function(n, dd, e, data){
48979         var result = false;
48980         if(data.header != n){
48981             result = this.positionIndicator(data.header, n, e);
48982         }
48983         if(!result){
48984             this.proxyTop.hide();
48985             this.proxyBottom.hide();
48986         }
48987         return result ? this.dropAllowed : this.dropNotAllowed;
48988     },
48989
48990     onNodeOut : function(n, dd, e, data){
48991         this.proxyTop.hide();
48992         this.proxyBottom.hide();
48993     },
48994
48995     onNodeDrop : function(n, dd, e, data){
48996         var h = data.header;
48997         if(h != n){
48998             var cm = this.grid.colModel;
48999             var x = Roo.lib.Event.getPageX(e);
49000             var r = Roo.lib.Dom.getRegion(n.firstChild);
49001             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
49002             var oldIndex = this.view.getCellIndex(h);
49003             var newIndex = this.view.getCellIndex(n);
49004             var locked = cm.isLocked(newIndex);
49005             if(pt == "after"){
49006                 newIndex++;
49007             }
49008             if(oldIndex < newIndex){
49009                 newIndex--;
49010             }
49011             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
49012                 return false;
49013             }
49014             cm.setLocked(oldIndex, locked, true);
49015             cm.moveColumn(oldIndex, newIndex);
49016             this.grid.fireEvent("columnmove", oldIndex, newIndex);
49017             return true;
49018         }
49019         return false;
49020     }
49021 });
49022 /*
49023  * Based on:
49024  * Ext JS Library 1.1.1
49025  * Copyright(c) 2006-2007, Ext JS, LLC.
49026  *
49027  * Originally Released Under LGPL - original licence link has changed is not relivant.
49028  *
49029  * Fork - LGPL
49030  * <script type="text/javascript">
49031  */
49032   
49033 /**
49034  * @class Roo.grid.GridView
49035  * @extends Roo.util.Observable
49036  *
49037  * @constructor
49038  * @param {Object} config
49039  */
49040 Roo.grid.GridView = function(config){
49041     Roo.grid.GridView.superclass.constructor.call(this);
49042     this.el = null;
49043
49044     Roo.apply(this, config);
49045 };
49046
49047 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
49048
49049     
49050     rowClass : "x-grid-row",
49051
49052     cellClass : "x-grid-col",
49053
49054     tdClass : "x-grid-td",
49055
49056     hdClass : "x-grid-hd",
49057
49058     splitClass : "x-grid-split",
49059
49060     sortClasses : ["sort-asc", "sort-desc"],
49061
49062     enableMoveAnim : false,
49063
49064     hlColor: "C3DAF9",
49065
49066     dh : Roo.DomHelper,
49067
49068     fly : Roo.Element.fly,
49069
49070     css : Roo.util.CSS,
49071
49072     borderWidth: 1,
49073
49074     splitOffset: 3,
49075
49076     scrollIncrement : 22,
49077
49078     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
49079
49080     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
49081
49082     bind : function(ds, cm){
49083         if(this.ds){
49084             this.ds.un("load", this.onLoad, this);
49085             this.ds.un("datachanged", this.onDataChange, this);
49086             this.ds.un("add", this.onAdd, this);
49087             this.ds.un("remove", this.onRemove, this);
49088             this.ds.un("update", this.onUpdate, this);
49089             this.ds.un("clear", this.onClear, this);
49090         }
49091         if(ds){
49092             ds.on("load", this.onLoad, this);
49093             ds.on("datachanged", this.onDataChange, this);
49094             ds.on("add", this.onAdd, this);
49095             ds.on("remove", this.onRemove, this);
49096             ds.on("update", this.onUpdate, this);
49097             ds.on("clear", this.onClear, this);
49098         }
49099         this.ds = ds;
49100
49101         if(this.cm){
49102             this.cm.un("widthchange", this.onColWidthChange, this);
49103             this.cm.un("headerchange", this.onHeaderChange, this);
49104             this.cm.un("hiddenchange", this.onHiddenChange, this);
49105             this.cm.un("columnmoved", this.onColumnMove, this);
49106             this.cm.un("columnlockchange", this.onColumnLock, this);
49107         }
49108         if(cm){
49109             this.generateRules(cm);
49110             cm.on("widthchange", this.onColWidthChange, this);
49111             cm.on("headerchange", this.onHeaderChange, this);
49112             cm.on("hiddenchange", this.onHiddenChange, this);
49113             cm.on("columnmoved", this.onColumnMove, this);
49114             cm.on("columnlockchange", this.onColumnLock, this);
49115         }
49116         this.cm = cm;
49117     },
49118
49119     init: function(grid){
49120         Roo.grid.GridView.superclass.init.call(this, grid);
49121
49122         this.bind(grid.dataSource, grid.colModel);
49123
49124         grid.on("headerclick", this.handleHeaderClick, this);
49125
49126         if(grid.trackMouseOver){
49127             grid.on("mouseover", this.onRowOver, this);
49128             grid.on("mouseout", this.onRowOut, this);
49129         }
49130         grid.cancelTextSelection = function(){};
49131         this.gridId = grid.id;
49132
49133         var tpls = this.templates || {};
49134
49135         if(!tpls.master){
49136             tpls.master = new Roo.Template(
49137                '<div class="x-grid" hidefocus="true">',
49138                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
49139                   '<div class="x-grid-topbar"></div>',
49140                   '<div class="x-grid-scroller"><div></div></div>',
49141                   '<div class="x-grid-locked">',
49142                       '<div class="x-grid-header">{lockedHeader}</div>',
49143                       '<div class="x-grid-body">{lockedBody}</div>',
49144                   "</div>",
49145                   '<div class="x-grid-viewport">',
49146                       '<div class="x-grid-header">{header}</div>',
49147                       '<div class="x-grid-body">{body}</div>',
49148                   "</div>",
49149                   '<div class="x-grid-bottombar"></div>',
49150                  
49151                   '<div class="x-grid-resize-proxy">&#160;</div>',
49152                "</div>"
49153             );
49154             tpls.master.disableformats = true;
49155         }
49156
49157         if(!tpls.header){
49158             tpls.header = new Roo.Template(
49159                '<table border="0" cellspacing="0" cellpadding="0">',
49160                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
49161                "</table>{splits}"
49162             );
49163             tpls.header.disableformats = true;
49164         }
49165         tpls.header.compile();
49166
49167         if(!tpls.hcell){
49168             tpls.hcell = new Roo.Template(
49169                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
49170                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
49171                 "</div></td>"
49172              );
49173              tpls.hcell.disableFormats = true;
49174         }
49175         tpls.hcell.compile();
49176
49177         if(!tpls.hsplit){
49178             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
49179             tpls.hsplit.disableFormats = true;
49180         }
49181         tpls.hsplit.compile();
49182
49183         if(!tpls.body){
49184             tpls.body = new Roo.Template(
49185                '<table border="0" cellspacing="0" cellpadding="0">',
49186                "<tbody>{rows}</tbody>",
49187                "</table>"
49188             );
49189             tpls.body.disableFormats = true;
49190         }
49191         tpls.body.compile();
49192
49193         if(!tpls.row){
49194             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
49195             tpls.row.disableFormats = true;
49196         }
49197         tpls.row.compile();
49198
49199         if(!tpls.cell){
49200             tpls.cell = new Roo.Template(
49201                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
49202                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
49203                 "</td>"
49204             );
49205             tpls.cell.disableFormats = true;
49206         }
49207         tpls.cell.compile();
49208
49209         this.templates = tpls;
49210     },
49211
49212     // remap these for backwards compat
49213     onColWidthChange : function(){
49214         this.updateColumns.apply(this, arguments);
49215     },
49216     onHeaderChange : function(){
49217         this.updateHeaders.apply(this, arguments);
49218     }, 
49219     onHiddenChange : function(){
49220         this.handleHiddenChange.apply(this, arguments);
49221     },
49222     onColumnMove : function(){
49223         this.handleColumnMove.apply(this, arguments);
49224     },
49225     onColumnLock : function(){
49226         this.handleLockChange.apply(this, arguments);
49227     },
49228
49229     onDataChange : function(){
49230         this.refresh();
49231         this.updateHeaderSortState();
49232     },
49233
49234     onClear : function(){
49235         this.refresh();
49236     },
49237
49238     onUpdate : function(ds, record){
49239         this.refreshRow(record);
49240     },
49241
49242     refreshRow : function(record){
49243         var ds = this.ds, index;
49244         if(typeof record == 'number'){
49245             index = record;
49246             record = ds.getAt(index);
49247         }else{
49248             index = ds.indexOf(record);
49249         }
49250         this.insertRows(ds, index, index, true);
49251         this.onRemove(ds, record, index+1, true);
49252         this.syncRowHeights(index, index);
49253         this.layout();
49254         this.fireEvent("rowupdated", this, index, record);
49255     },
49256
49257     onAdd : function(ds, records, index){
49258         this.insertRows(ds, index, index + (records.length-1));
49259     },
49260
49261     onRemove : function(ds, record, index, isUpdate){
49262         if(isUpdate !== true){
49263             this.fireEvent("beforerowremoved", this, index, record);
49264         }
49265         var bt = this.getBodyTable(), lt = this.getLockedTable();
49266         if(bt.rows[index]){
49267             bt.firstChild.removeChild(bt.rows[index]);
49268         }
49269         if(lt.rows[index]){
49270             lt.firstChild.removeChild(lt.rows[index]);
49271         }
49272         if(isUpdate !== true){
49273             this.stripeRows(index);
49274             this.syncRowHeights(index, index);
49275             this.layout();
49276             this.fireEvent("rowremoved", this, index, record);
49277         }
49278     },
49279
49280     onLoad : function(){
49281         this.scrollToTop();
49282     },
49283
49284     /**
49285      * Scrolls the grid to the top
49286      */
49287     scrollToTop : function(){
49288         if(this.scroller){
49289             this.scroller.dom.scrollTop = 0;
49290             this.syncScroll();
49291         }
49292     },
49293
49294     /**
49295      * Gets a panel in the header of the grid that can be used for toolbars etc.
49296      * After modifying the contents of this panel a call to grid.autoSize() may be
49297      * required to register any changes in size.
49298      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
49299      * @return Roo.Element
49300      */
49301     getHeaderPanel : function(doShow){
49302         if(doShow){
49303             this.headerPanel.show();
49304         }
49305         return this.headerPanel;
49306     },
49307
49308     /**
49309      * Gets a panel in the footer of the grid that can be used for toolbars etc.
49310      * After modifying the contents of this panel a call to grid.autoSize() may be
49311      * required to register any changes in size.
49312      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
49313      * @return Roo.Element
49314      */
49315     getFooterPanel : function(doShow){
49316         if(doShow){
49317             this.footerPanel.show();
49318         }
49319         return this.footerPanel;
49320     },
49321
49322     initElements : function(){
49323         var E = Roo.Element;
49324         var el = this.grid.getGridEl().dom.firstChild;
49325         var cs = el.childNodes;
49326
49327         this.el = new E(el);
49328         
49329          this.focusEl = new E(el.firstChild);
49330         this.focusEl.swallowEvent("click", true);
49331         
49332         this.headerPanel = new E(cs[1]);
49333         this.headerPanel.enableDisplayMode("block");
49334
49335         this.scroller = new E(cs[2]);
49336         this.scrollSizer = new E(this.scroller.dom.firstChild);
49337
49338         this.lockedWrap = new E(cs[3]);
49339         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
49340         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
49341
49342         this.mainWrap = new E(cs[4]);
49343         this.mainHd = new E(this.mainWrap.dom.firstChild);
49344         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
49345
49346         this.footerPanel = new E(cs[5]);
49347         this.footerPanel.enableDisplayMode("block");
49348
49349         this.resizeProxy = new E(cs[6]);
49350
49351         this.headerSelector = String.format(
49352            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
49353            this.lockedHd.id, this.mainHd.id
49354         );
49355
49356         this.splitterSelector = String.format(
49357            '#{0} div.x-grid-split, #{1} div.x-grid-split',
49358            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
49359         );
49360     },
49361     idToCssName : function(s)
49362     {
49363         return s.replace(/[^a-z0-9]+/ig, '-');
49364     },
49365
49366     getHeaderCell : function(index){
49367         return Roo.DomQuery.select(this.headerSelector)[index];
49368     },
49369
49370     getHeaderCellMeasure : function(index){
49371         return this.getHeaderCell(index).firstChild;
49372     },
49373
49374     getHeaderCellText : function(index){
49375         return this.getHeaderCell(index).firstChild.firstChild;
49376     },
49377
49378     getLockedTable : function(){
49379         return this.lockedBody.dom.firstChild;
49380     },
49381
49382     getBodyTable : function(){
49383         return this.mainBody.dom.firstChild;
49384     },
49385
49386     getLockedRow : function(index){
49387         return this.getLockedTable().rows[index];
49388     },
49389
49390     getRow : function(index){
49391         return this.getBodyTable().rows[index];
49392     },
49393
49394     getRowComposite : function(index){
49395         if(!this.rowEl){
49396             this.rowEl = new Roo.CompositeElementLite();
49397         }
49398         var els = [], lrow, mrow;
49399         if(lrow = this.getLockedRow(index)){
49400             els.push(lrow);
49401         }
49402         if(mrow = this.getRow(index)){
49403             els.push(mrow);
49404         }
49405         this.rowEl.elements = els;
49406         return this.rowEl;
49407     },
49408     /**
49409      * Gets the 'td' of the cell
49410      * 
49411      * @param {Integer} rowIndex row to select
49412      * @param {Integer} colIndex column to select
49413      * 
49414      * @return {Object} 
49415      */
49416     getCell : function(rowIndex, colIndex){
49417         var locked = this.cm.getLockedCount();
49418         var source;
49419         if(colIndex < locked){
49420             source = this.lockedBody.dom.firstChild;
49421         }else{
49422             source = this.mainBody.dom.firstChild;
49423             colIndex -= locked;
49424         }
49425         return source.rows[rowIndex].childNodes[colIndex];
49426     },
49427
49428     getCellText : function(rowIndex, colIndex){
49429         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
49430     },
49431
49432     getCellBox : function(cell){
49433         var b = this.fly(cell).getBox();
49434         if(Roo.isOpera){ // opera fails to report the Y
49435             b.y = cell.offsetTop + this.mainBody.getY();
49436         }
49437         return b;
49438     },
49439
49440     getCellIndex : function(cell){
49441         var id = String(cell.className).match(this.cellRE);
49442         if(id){
49443             return parseInt(id[1], 10);
49444         }
49445         return 0;
49446     },
49447
49448     findHeaderIndex : function(n){
49449         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49450         return r ? this.getCellIndex(r) : false;
49451     },
49452
49453     findHeaderCell : function(n){
49454         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49455         return r ? r : false;
49456     },
49457
49458     findRowIndex : function(n){
49459         if(!n){
49460             return false;
49461         }
49462         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
49463         return r ? r.rowIndex : false;
49464     },
49465
49466     findCellIndex : function(node){
49467         var stop = this.el.dom;
49468         while(node && node != stop){
49469             if(this.findRE.test(node.className)){
49470                 return this.getCellIndex(node);
49471             }
49472             node = node.parentNode;
49473         }
49474         return false;
49475     },
49476
49477     getColumnId : function(index){
49478         return this.cm.getColumnId(index);
49479     },
49480
49481     getSplitters : function()
49482     {
49483         if(this.splitterSelector){
49484            return Roo.DomQuery.select(this.splitterSelector);
49485         }else{
49486             return null;
49487       }
49488     },
49489
49490     getSplitter : function(index){
49491         return this.getSplitters()[index];
49492     },
49493
49494     onRowOver : function(e, t){
49495         var row;
49496         if((row = this.findRowIndex(t)) !== false){
49497             this.getRowComposite(row).addClass("x-grid-row-over");
49498         }
49499     },
49500
49501     onRowOut : function(e, t){
49502         var row;
49503         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
49504             this.getRowComposite(row).removeClass("x-grid-row-over");
49505         }
49506     },
49507
49508     renderHeaders : function(){
49509         var cm = this.cm;
49510         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
49511         var cb = [], lb = [], sb = [], lsb = [], p = {};
49512         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49513             p.cellId = "x-grid-hd-0-" + i;
49514             p.splitId = "x-grid-csplit-0-" + i;
49515             p.id = cm.getColumnId(i);
49516             p.title = cm.getColumnTooltip(i) || "";
49517             p.value = cm.getColumnHeader(i) || "";
49518             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
49519             if(!cm.isLocked(i)){
49520                 cb[cb.length] = ct.apply(p);
49521                 sb[sb.length] = st.apply(p);
49522             }else{
49523                 lb[lb.length] = ct.apply(p);
49524                 lsb[lsb.length] = st.apply(p);
49525             }
49526         }
49527         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
49528                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
49529     },
49530
49531     updateHeaders : function(){
49532         var html = this.renderHeaders();
49533         this.lockedHd.update(html[0]);
49534         this.mainHd.update(html[1]);
49535     },
49536
49537     /**
49538      * Focuses the specified row.
49539      * @param {Number} row The row index
49540      */
49541     focusRow : function(row)
49542     {
49543         //Roo.log('GridView.focusRow');
49544         var x = this.scroller.dom.scrollLeft;
49545         this.focusCell(row, 0, false);
49546         this.scroller.dom.scrollLeft = x;
49547     },
49548
49549     /**
49550      * Focuses the specified cell.
49551      * @param {Number} row The row index
49552      * @param {Number} col The column index
49553      * @param {Boolean} hscroll false to disable horizontal scrolling
49554      */
49555     focusCell : function(row, col, hscroll)
49556     {
49557         //Roo.log('GridView.focusCell');
49558         var el = this.ensureVisible(row, col, hscroll);
49559         this.focusEl.alignTo(el, "tl-tl");
49560         if(Roo.isGecko){
49561             this.focusEl.focus();
49562         }else{
49563             this.focusEl.focus.defer(1, this.focusEl);
49564         }
49565     },
49566
49567     /**
49568      * Scrolls the specified cell into view
49569      * @param {Number} row The row index
49570      * @param {Number} col The column index
49571      * @param {Boolean} hscroll false to disable horizontal scrolling
49572      */
49573     ensureVisible : function(row, col, hscroll)
49574     {
49575         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
49576         //return null; //disable for testing.
49577         if(typeof row != "number"){
49578             row = row.rowIndex;
49579         }
49580         if(row < 0 && row >= this.ds.getCount()){
49581             return  null;
49582         }
49583         col = (col !== undefined ? col : 0);
49584         var cm = this.grid.colModel;
49585         while(cm.isHidden(col)){
49586             col++;
49587         }
49588
49589         var el = this.getCell(row, col);
49590         if(!el){
49591             return null;
49592         }
49593         var c = this.scroller.dom;
49594
49595         var ctop = parseInt(el.offsetTop, 10);
49596         var cleft = parseInt(el.offsetLeft, 10);
49597         var cbot = ctop + el.offsetHeight;
49598         var cright = cleft + el.offsetWidth;
49599         
49600         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
49601         var stop = parseInt(c.scrollTop, 10);
49602         var sleft = parseInt(c.scrollLeft, 10);
49603         var sbot = stop + ch;
49604         var sright = sleft + c.clientWidth;
49605         /*
49606         Roo.log('GridView.ensureVisible:' +
49607                 ' ctop:' + ctop +
49608                 ' c.clientHeight:' + c.clientHeight +
49609                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
49610                 ' stop:' + stop +
49611                 ' cbot:' + cbot +
49612                 ' sbot:' + sbot +
49613                 ' ch:' + ch  
49614                 );
49615         */
49616         if(ctop < stop){
49617              c.scrollTop = ctop;
49618             //Roo.log("set scrolltop to ctop DISABLE?");
49619         }else if(cbot > sbot){
49620             //Roo.log("set scrolltop to cbot-ch");
49621             c.scrollTop = cbot-ch;
49622         }
49623         
49624         if(hscroll !== false){
49625             if(cleft < sleft){
49626                 c.scrollLeft = cleft;
49627             }else if(cright > sright){
49628                 c.scrollLeft = cright-c.clientWidth;
49629             }
49630         }
49631          
49632         return el;
49633     },
49634
49635     updateColumns : function(){
49636         this.grid.stopEditing();
49637         var cm = this.grid.colModel, colIds = this.getColumnIds();
49638         //var totalWidth = cm.getTotalWidth();
49639         var pos = 0;
49640         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49641             //if(cm.isHidden(i)) continue;
49642             var w = cm.getColumnWidth(i);
49643             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49644             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49645         }
49646         this.updateSplitters();
49647     },
49648
49649     generateRules : function(cm){
49650         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49651         Roo.util.CSS.removeStyleSheet(rulesId);
49652         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49653             var cid = cm.getColumnId(i);
49654             var align = '';
49655             if(cm.config[i].align){
49656                 align = 'text-align:'+cm.config[i].align+';';
49657             }
49658             var hidden = '';
49659             if(cm.isHidden(i)){
49660                 hidden = 'display:none;';
49661             }
49662             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49663             ruleBuf.push(
49664                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49665                     this.hdSelector, cid, " {\n", align, width, "}\n",
49666                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49667                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49668         }
49669         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49670     },
49671
49672     updateSplitters : function(){
49673         var cm = this.cm, s = this.getSplitters();
49674         if(s){ // splitters not created yet
49675             var pos = 0, locked = true;
49676             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49677                 if(cm.isHidden(i)) continue;
49678                 var w = cm.getColumnWidth(i); // make sure it's a number
49679                 if(!cm.isLocked(i) && locked){
49680                     pos = 0;
49681                     locked = false;
49682                 }
49683                 pos += w;
49684                 s[i].style.left = (pos-this.splitOffset) + "px";
49685             }
49686         }
49687     },
49688
49689     handleHiddenChange : function(colModel, colIndex, hidden){
49690         if(hidden){
49691             this.hideColumn(colIndex);
49692         }else{
49693             this.unhideColumn(colIndex);
49694         }
49695     },
49696
49697     hideColumn : function(colIndex){
49698         var cid = this.getColumnId(colIndex);
49699         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49700         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49701         if(Roo.isSafari){
49702             this.updateHeaders();
49703         }
49704         this.updateSplitters();
49705         this.layout();
49706     },
49707
49708     unhideColumn : function(colIndex){
49709         var cid = this.getColumnId(colIndex);
49710         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49711         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49712
49713         if(Roo.isSafari){
49714             this.updateHeaders();
49715         }
49716         this.updateSplitters();
49717         this.layout();
49718     },
49719
49720     insertRows : function(dm, firstRow, lastRow, isUpdate){
49721         if(firstRow == 0 && lastRow == dm.getCount()-1){
49722             this.refresh();
49723         }else{
49724             if(!isUpdate){
49725                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49726             }
49727             var s = this.getScrollState();
49728             var markup = this.renderRows(firstRow, lastRow);
49729             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49730             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49731             this.restoreScroll(s);
49732             if(!isUpdate){
49733                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49734                 this.syncRowHeights(firstRow, lastRow);
49735                 this.stripeRows(firstRow);
49736                 this.layout();
49737             }
49738         }
49739     },
49740
49741     bufferRows : function(markup, target, index){
49742         var before = null, trows = target.rows, tbody = target.tBodies[0];
49743         if(index < trows.length){
49744             before = trows[index];
49745         }
49746         var b = document.createElement("div");
49747         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49748         var rows = b.firstChild.rows;
49749         for(var i = 0, len = rows.length; i < len; i++){
49750             if(before){
49751                 tbody.insertBefore(rows[0], before);
49752             }else{
49753                 tbody.appendChild(rows[0]);
49754             }
49755         }
49756         b.innerHTML = "";
49757         b = null;
49758     },
49759
49760     deleteRows : function(dm, firstRow, lastRow){
49761         if(dm.getRowCount()<1){
49762             this.fireEvent("beforerefresh", this);
49763             this.mainBody.update("");
49764             this.lockedBody.update("");
49765             this.fireEvent("refresh", this);
49766         }else{
49767             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49768             var bt = this.getBodyTable();
49769             var tbody = bt.firstChild;
49770             var rows = bt.rows;
49771             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49772                 tbody.removeChild(rows[firstRow]);
49773             }
49774             this.stripeRows(firstRow);
49775             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49776         }
49777     },
49778
49779     updateRows : function(dataSource, firstRow, lastRow){
49780         var s = this.getScrollState();
49781         this.refresh();
49782         this.restoreScroll(s);
49783     },
49784
49785     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49786         if(!noRefresh){
49787            this.refresh();
49788         }
49789         this.updateHeaderSortState();
49790     },
49791
49792     getScrollState : function(){
49793         
49794         var sb = this.scroller.dom;
49795         return {left: sb.scrollLeft, top: sb.scrollTop};
49796     },
49797
49798     stripeRows : function(startRow){
49799         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49800             return;
49801         }
49802         startRow = startRow || 0;
49803         var rows = this.getBodyTable().rows;
49804         var lrows = this.getLockedTable().rows;
49805         var cls = ' x-grid-row-alt ';
49806         for(var i = startRow, len = rows.length; i < len; i++){
49807             var row = rows[i], lrow = lrows[i];
49808             var isAlt = ((i+1) % 2 == 0);
49809             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49810             if(isAlt == hasAlt){
49811                 continue;
49812             }
49813             if(isAlt){
49814                 row.className += " x-grid-row-alt";
49815             }else{
49816                 row.className = row.className.replace("x-grid-row-alt", "");
49817             }
49818             if(lrow){
49819                 lrow.className = row.className;
49820             }
49821         }
49822     },
49823
49824     restoreScroll : function(state){
49825         //Roo.log('GridView.restoreScroll');
49826         var sb = this.scroller.dom;
49827         sb.scrollLeft = state.left;
49828         sb.scrollTop = state.top;
49829         this.syncScroll();
49830     },
49831
49832     syncScroll : function(){
49833         //Roo.log('GridView.syncScroll');
49834         var sb = this.scroller.dom;
49835         var sh = this.mainHd.dom;
49836         var bs = this.mainBody.dom;
49837         var lv = this.lockedBody.dom;
49838         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49839         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49840     },
49841
49842     handleScroll : function(e){
49843         this.syncScroll();
49844         var sb = this.scroller.dom;
49845         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49846         e.stopEvent();
49847     },
49848
49849     handleWheel : function(e){
49850         var d = e.getWheelDelta();
49851         this.scroller.dom.scrollTop -= d*22;
49852         // set this here to prevent jumpy scrolling on large tables
49853         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49854         e.stopEvent();
49855     },
49856
49857     renderRows : function(startRow, endRow){
49858         // pull in all the crap needed to render rows
49859         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49860         var colCount = cm.getColumnCount();
49861
49862         if(ds.getCount() < 1){
49863             return ["", ""];
49864         }
49865
49866         // build a map for all the columns
49867         var cs = [];
49868         for(var i = 0; i < colCount; i++){
49869             var name = cm.getDataIndex(i);
49870             cs[i] = {
49871                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49872                 renderer : cm.getRenderer(i),
49873                 id : cm.getColumnId(i),
49874                 locked : cm.isLocked(i)
49875             };
49876         }
49877
49878         startRow = startRow || 0;
49879         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49880
49881         // records to render
49882         var rs = ds.getRange(startRow, endRow);
49883
49884         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49885     },
49886
49887     // As much as I hate to duplicate code, this was branched because FireFox really hates
49888     // [].join("") on strings. The performance difference was substantial enough to
49889     // branch this function
49890     doRender : Roo.isGecko ?
49891             function(cs, rs, ds, startRow, colCount, stripe){
49892                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49893                 // buffers
49894                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49895                 
49896                 var hasListener = this.grid.hasListener('rowclass');
49897                 var rowcfg = {};
49898                 for(var j = 0, len = rs.length; j < len; j++){
49899                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49900                     for(var i = 0; i < colCount; i++){
49901                         c = cs[i];
49902                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49903                         p.id = c.id;
49904                         p.css = p.attr = "";
49905                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49906                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49907                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49908                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49909                         }
49910                         var markup = ct.apply(p);
49911                         if(!c.locked){
49912                             cb+= markup;
49913                         }else{
49914                             lcb+= markup;
49915                         }
49916                     }
49917                     var alt = [];
49918                     if(stripe && ((rowIndex+1) % 2 == 0)){
49919                         alt.push("x-grid-row-alt")
49920                     }
49921                     if(r.dirty){
49922                         alt.push(  " x-grid-dirty-row");
49923                     }
49924                     rp.cells = lcb;
49925                     if(this.getRowClass){
49926                         alt.push(this.getRowClass(r, rowIndex));
49927                     }
49928                     if (hasListener) {
49929                         rowcfg = {
49930                              
49931                             record: r,
49932                             rowIndex : rowIndex,
49933                             rowClass : ''
49934                         }
49935                         this.grid.fireEvent('rowclass', this, rowcfg);
49936                         alt.push(rowcfg.rowClass);
49937                     }
49938                     rp.alt = alt.join(" ");
49939                     lbuf+= rt.apply(rp);
49940                     rp.cells = cb;
49941                     buf+=  rt.apply(rp);
49942                 }
49943                 return [lbuf, buf];
49944             } :
49945             function(cs, rs, ds, startRow, colCount, stripe){
49946                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49947                 // buffers
49948                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49949                 var hasListener = this.grid.hasListener('rowclass');
49950  
49951                 var rowcfg = {};
49952                 for(var j = 0, len = rs.length; j < len; j++){
49953                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49954                     for(var i = 0; i < colCount; i++){
49955                         c = cs[i];
49956                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49957                         p.id = c.id;
49958                         p.css = p.attr = "";
49959                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49960                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49961                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49962                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49963                         }
49964                         
49965                         var markup = ct.apply(p);
49966                         if(!c.locked){
49967                             cb[cb.length] = markup;
49968                         }else{
49969                             lcb[lcb.length] = markup;
49970                         }
49971                     }
49972                     var alt = [];
49973                     if(stripe && ((rowIndex+1) % 2 == 0)){
49974                         alt.push( "x-grid-row-alt");
49975                     }
49976                     if(r.dirty){
49977                         alt.push(" x-grid-dirty-row");
49978                     }
49979                     rp.cells = lcb;
49980                     if(this.getRowClass){
49981                         alt.push( this.getRowClass(r, rowIndex));
49982                     }
49983                     if (hasListener) {
49984                         rowcfg = {
49985                              
49986                             record: r,
49987                             rowIndex : rowIndex,
49988                             rowClass : ''
49989                         }
49990                         this.grid.fireEvent('rowclass', this, rowcfg);
49991                         alt.push(rowcfg.rowClass);
49992                     }
49993                     rp.alt = alt.join(" ");
49994                     rp.cells = lcb.join("");
49995                     lbuf[lbuf.length] = rt.apply(rp);
49996                     rp.cells = cb.join("");
49997                     buf[buf.length] =  rt.apply(rp);
49998                 }
49999                 return [lbuf.join(""), buf.join("")];
50000             },
50001
50002     renderBody : function(){
50003         var markup = this.renderRows();
50004         var bt = this.templates.body;
50005         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
50006     },
50007
50008     /**
50009      * Refreshes the grid
50010      * @param {Boolean} headersToo
50011      */
50012     refresh : function(headersToo){
50013         this.fireEvent("beforerefresh", this);
50014         this.grid.stopEditing();
50015         var result = this.renderBody();
50016         this.lockedBody.update(result[0]);
50017         this.mainBody.update(result[1]);
50018         if(headersToo === true){
50019             this.updateHeaders();
50020             this.updateColumns();
50021             this.updateSplitters();
50022             this.updateHeaderSortState();
50023         }
50024         this.syncRowHeights();
50025         this.layout();
50026         this.fireEvent("refresh", this);
50027     },
50028
50029     handleColumnMove : function(cm, oldIndex, newIndex){
50030         this.indexMap = null;
50031         var s = this.getScrollState();
50032         this.refresh(true);
50033         this.restoreScroll(s);
50034         this.afterMove(newIndex);
50035     },
50036
50037     afterMove : function(colIndex){
50038         if(this.enableMoveAnim && Roo.enableFx){
50039             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
50040         }
50041         // if multisort - fix sortOrder, and reload..
50042         if (this.grid.dataSource.multiSort) {
50043             // the we can call sort again..
50044             var dm = this.grid.dataSource;
50045             var cm = this.grid.colModel;
50046             var so = [];
50047             for(var i = 0; i < cm.config.length; i++ ) {
50048                 
50049                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
50050                     continue; // dont' bother, it's not in sort list or being set.
50051                 }
50052                 
50053                 so.push(cm.config[i].dataIndex);
50054             };
50055             dm.sortOrder = so;
50056             dm.load(dm.lastOptions);
50057             
50058             
50059         }
50060         
50061     },
50062
50063     updateCell : function(dm, rowIndex, dataIndex){
50064         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
50065         if(typeof colIndex == "undefined"){ // not present in grid
50066             return;
50067         }
50068         var cm = this.grid.colModel;
50069         var cell = this.getCell(rowIndex, colIndex);
50070         var cellText = this.getCellText(rowIndex, colIndex);
50071
50072         var p = {
50073             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
50074             id : cm.getColumnId(colIndex),
50075             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
50076         };
50077         var renderer = cm.getRenderer(colIndex);
50078         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
50079         if(typeof val == "undefined" || val === "") val = "&#160;";
50080         cellText.innerHTML = val;
50081         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
50082         this.syncRowHeights(rowIndex, rowIndex);
50083     },
50084
50085     calcColumnWidth : function(colIndex, maxRowsToMeasure){
50086         var maxWidth = 0;
50087         if(this.grid.autoSizeHeaders){
50088             var h = this.getHeaderCellMeasure(colIndex);
50089             maxWidth = Math.max(maxWidth, h.scrollWidth);
50090         }
50091         var tb, index;
50092         if(this.cm.isLocked(colIndex)){
50093             tb = this.getLockedTable();
50094             index = colIndex;
50095         }else{
50096             tb = this.getBodyTable();
50097             index = colIndex - this.cm.getLockedCount();
50098         }
50099         if(tb && tb.rows){
50100             var rows = tb.rows;
50101             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
50102             for(var i = 0; i < stopIndex; i++){
50103                 var cell = rows[i].childNodes[index].firstChild;
50104                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
50105             }
50106         }
50107         return maxWidth + /*margin for error in IE*/ 5;
50108     },
50109     /**
50110      * Autofit a column to its content.
50111      * @param {Number} colIndex
50112      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
50113      */
50114      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
50115          if(this.cm.isHidden(colIndex)){
50116              return; // can't calc a hidden column
50117          }
50118         if(forceMinSize){
50119             var cid = this.cm.getColumnId(colIndex);
50120             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
50121            if(this.grid.autoSizeHeaders){
50122                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
50123            }
50124         }
50125         var newWidth = this.calcColumnWidth(colIndex);
50126         this.cm.setColumnWidth(colIndex,
50127             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
50128         if(!suppressEvent){
50129             this.grid.fireEvent("columnresize", colIndex, newWidth);
50130         }
50131     },
50132
50133     /**
50134      * Autofits all columns to their content and then expands to fit any extra space in the grid
50135      */
50136      autoSizeColumns : function(){
50137         var cm = this.grid.colModel;
50138         var colCount = cm.getColumnCount();
50139         for(var i = 0; i < colCount; i++){
50140             this.autoSizeColumn(i, true, true);
50141         }
50142         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
50143             this.fitColumns();
50144         }else{
50145             this.updateColumns();
50146             this.layout();
50147         }
50148     },
50149
50150     /**
50151      * Autofits all columns to the grid's width proportionate with their current size
50152      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
50153      */
50154     fitColumns : function(reserveScrollSpace){
50155         var cm = this.grid.colModel;
50156         var colCount = cm.getColumnCount();
50157         var cols = [];
50158         var width = 0;
50159         var i, w;
50160         for (i = 0; i < colCount; i++){
50161             if(!cm.isHidden(i) && !cm.isFixed(i)){
50162                 w = cm.getColumnWidth(i);
50163                 cols.push(i);
50164                 cols.push(w);
50165                 width += w;
50166             }
50167         }
50168         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
50169         if(reserveScrollSpace){
50170             avail -= 17;
50171         }
50172         var frac = (avail - cm.getTotalWidth())/width;
50173         while (cols.length){
50174             w = cols.pop();
50175             i = cols.pop();
50176             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
50177         }
50178         this.updateColumns();
50179         this.layout();
50180     },
50181
50182     onRowSelect : function(rowIndex){
50183         var row = this.getRowComposite(rowIndex);
50184         row.addClass("x-grid-row-selected");
50185     },
50186
50187     onRowDeselect : function(rowIndex){
50188         var row = this.getRowComposite(rowIndex);
50189         row.removeClass("x-grid-row-selected");
50190     },
50191
50192     onCellSelect : function(row, col){
50193         var cell = this.getCell(row, col);
50194         if(cell){
50195             Roo.fly(cell).addClass("x-grid-cell-selected");
50196         }
50197     },
50198
50199     onCellDeselect : function(row, col){
50200         var cell = this.getCell(row, col);
50201         if(cell){
50202             Roo.fly(cell).removeClass("x-grid-cell-selected");
50203         }
50204     },
50205
50206     updateHeaderSortState : function(){
50207         
50208         // sort state can be single { field: xxx, direction : yyy}
50209         // or   { xxx=>ASC , yyy : DESC ..... }
50210         
50211         var mstate = {};
50212         if (!this.ds.multiSort) { 
50213             var state = this.ds.getSortState();
50214             if(!state){
50215                 return;
50216             }
50217             mstate[state.field] = state.direction;
50218             // FIXME... - this is not used here.. but might be elsewhere..
50219             this.sortState = state;
50220             
50221         } else {
50222             mstate = this.ds.sortToggle;
50223         }
50224         //remove existing sort classes..
50225         
50226         var sc = this.sortClasses;
50227         var hds = this.el.select(this.headerSelector).removeClass(sc);
50228         
50229         for(var f in mstate) {
50230         
50231             var sortColumn = this.cm.findColumnIndex(f);
50232             
50233             if(sortColumn != -1){
50234                 var sortDir = mstate[f];        
50235                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
50236             }
50237         }
50238         
50239          
50240         
50241     },
50242
50243
50244     handleHeaderClick : function(g, index){
50245         if(this.headersDisabled){
50246             return;
50247         }
50248         var dm = g.dataSource, cm = g.colModel;
50249         if(!cm.isSortable(index)){
50250             return;
50251         }
50252         g.stopEditing();
50253         
50254         if (dm.multiSort) {
50255             // update the sortOrder
50256             var so = [];
50257             for(var i = 0; i < cm.config.length; i++ ) {
50258                 
50259                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
50260                     continue; // dont' bother, it's not in sort list or being set.
50261                 }
50262                 
50263                 so.push(cm.config[i].dataIndex);
50264             };
50265             dm.sortOrder = so;
50266         }
50267         
50268         
50269         dm.sort(cm.getDataIndex(index));
50270     },
50271
50272
50273     destroy : function(){
50274         if(this.colMenu){
50275             this.colMenu.removeAll();
50276             Roo.menu.MenuMgr.unregister(this.colMenu);
50277             this.colMenu.getEl().remove();
50278             delete this.colMenu;
50279         }
50280         if(this.hmenu){
50281             this.hmenu.removeAll();
50282             Roo.menu.MenuMgr.unregister(this.hmenu);
50283             this.hmenu.getEl().remove();
50284             delete this.hmenu;
50285         }
50286         if(this.grid.enableColumnMove){
50287             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
50288             if(dds){
50289                 for(var dd in dds){
50290                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
50291                         var elid = dds[dd].dragElId;
50292                         dds[dd].unreg();
50293                         Roo.get(elid).remove();
50294                     } else if(dds[dd].config.isTarget){
50295                         dds[dd].proxyTop.remove();
50296                         dds[dd].proxyBottom.remove();
50297                         dds[dd].unreg();
50298                     }
50299                     if(Roo.dd.DDM.locationCache[dd]){
50300                         delete Roo.dd.DDM.locationCache[dd];
50301                     }
50302                 }
50303                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
50304             }
50305         }
50306         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
50307         this.bind(null, null);
50308         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
50309     },
50310
50311     handleLockChange : function(){
50312         this.refresh(true);
50313     },
50314
50315     onDenyColumnLock : function(){
50316
50317     },
50318
50319     onDenyColumnHide : function(){
50320
50321     },
50322
50323     handleHdMenuClick : function(item){
50324         var index = this.hdCtxIndex;
50325         var cm = this.cm, ds = this.ds;
50326         switch(item.id){
50327             case "asc":
50328                 ds.sort(cm.getDataIndex(index), "ASC");
50329                 break;
50330             case "desc":
50331                 ds.sort(cm.getDataIndex(index), "DESC");
50332                 break;
50333             case "lock":
50334                 var lc = cm.getLockedCount();
50335                 if(cm.getColumnCount(true) <= lc+1){
50336                     this.onDenyColumnLock();
50337                     return;
50338                 }
50339                 if(lc != index){
50340                     cm.setLocked(index, true, true);
50341                     cm.moveColumn(index, lc);
50342                     this.grid.fireEvent("columnmove", index, lc);
50343                 }else{
50344                     cm.setLocked(index, true);
50345                 }
50346             break;
50347             case "unlock":
50348                 var lc = cm.getLockedCount();
50349                 if((lc-1) != index){
50350                     cm.setLocked(index, false, true);
50351                     cm.moveColumn(index, lc-1);
50352                     this.grid.fireEvent("columnmove", index, lc-1);
50353                 }else{
50354                     cm.setLocked(index, false);
50355                 }
50356             break;
50357             default:
50358                 index = cm.getIndexById(item.id.substr(4));
50359                 if(index != -1){
50360                     if(item.checked && cm.getColumnCount(true) <= 1){
50361                         this.onDenyColumnHide();
50362                         return false;
50363                     }
50364                     cm.setHidden(index, item.checked);
50365                 }
50366         }
50367         return true;
50368     },
50369
50370     beforeColMenuShow : function(){
50371         var cm = this.cm,  colCount = cm.getColumnCount();
50372         this.colMenu.removeAll();
50373         for(var i = 0; i < colCount; i++){
50374             this.colMenu.add(new Roo.menu.CheckItem({
50375                 id: "col-"+cm.getColumnId(i),
50376                 text: cm.getColumnHeader(i),
50377                 checked: !cm.isHidden(i),
50378                 hideOnClick:false
50379             }));
50380         }
50381     },
50382
50383     handleHdCtx : function(g, index, e){
50384         e.stopEvent();
50385         var hd = this.getHeaderCell(index);
50386         this.hdCtxIndex = index;
50387         var ms = this.hmenu.items, cm = this.cm;
50388         ms.get("asc").setDisabled(!cm.isSortable(index));
50389         ms.get("desc").setDisabled(!cm.isSortable(index));
50390         if(this.grid.enableColLock !== false){
50391             ms.get("lock").setDisabled(cm.isLocked(index));
50392             ms.get("unlock").setDisabled(!cm.isLocked(index));
50393         }
50394         this.hmenu.show(hd, "tl-bl");
50395     },
50396
50397     handleHdOver : function(e){
50398         var hd = this.findHeaderCell(e.getTarget());
50399         if(hd && !this.headersDisabled){
50400             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
50401                this.fly(hd).addClass("x-grid-hd-over");
50402             }
50403         }
50404     },
50405
50406     handleHdOut : function(e){
50407         var hd = this.findHeaderCell(e.getTarget());
50408         if(hd){
50409             this.fly(hd).removeClass("x-grid-hd-over");
50410         }
50411     },
50412
50413     handleSplitDblClick : function(e, t){
50414         var i = this.getCellIndex(t);
50415         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
50416             this.autoSizeColumn(i, true);
50417             this.layout();
50418         }
50419     },
50420
50421     render : function(){
50422
50423         var cm = this.cm;
50424         var colCount = cm.getColumnCount();
50425
50426         if(this.grid.monitorWindowResize === true){
50427             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50428         }
50429         var header = this.renderHeaders();
50430         var body = this.templates.body.apply({rows:""});
50431         var html = this.templates.master.apply({
50432             lockedBody: body,
50433             body: body,
50434             lockedHeader: header[0],
50435             header: header[1]
50436         });
50437
50438         //this.updateColumns();
50439
50440         this.grid.getGridEl().dom.innerHTML = html;
50441
50442         this.initElements();
50443         
50444         // a kludge to fix the random scolling effect in webkit
50445         this.el.on("scroll", function() {
50446             this.el.dom.scrollTop=0; // hopefully not recursive..
50447         },this);
50448
50449         this.scroller.on("scroll", this.handleScroll, this);
50450         this.lockedBody.on("mousewheel", this.handleWheel, this);
50451         this.mainBody.on("mousewheel", this.handleWheel, this);
50452
50453         this.mainHd.on("mouseover", this.handleHdOver, this);
50454         this.mainHd.on("mouseout", this.handleHdOut, this);
50455         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
50456                 {delegate: "."+this.splitClass});
50457
50458         this.lockedHd.on("mouseover", this.handleHdOver, this);
50459         this.lockedHd.on("mouseout", this.handleHdOut, this);
50460         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
50461                 {delegate: "."+this.splitClass});
50462
50463         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
50464             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50465         }
50466
50467         this.updateSplitters();
50468
50469         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
50470             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50471             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50472         }
50473
50474         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
50475             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
50476             this.hmenu.add(
50477                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
50478                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
50479             );
50480             if(this.grid.enableColLock !== false){
50481                 this.hmenu.add('-',
50482                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
50483                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
50484                 );
50485             }
50486             if(this.grid.enableColumnHide !== false){
50487
50488                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
50489                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
50490                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
50491
50492                 this.hmenu.add('-',
50493                     {id:"columns", text: this.columnsText, menu: this.colMenu}
50494                 );
50495             }
50496             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
50497
50498             this.grid.on("headercontextmenu", this.handleHdCtx, this);
50499         }
50500
50501         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
50502             this.dd = new Roo.grid.GridDragZone(this.grid, {
50503                 ddGroup : this.grid.ddGroup || 'GridDD'
50504             });
50505         }
50506
50507         /*
50508         for(var i = 0; i < colCount; i++){
50509             if(cm.isHidden(i)){
50510                 this.hideColumn(i);
50511             }
50512             if(cm.config[i].align){
50513                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
50514                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
50515             }
50516         }*/
50517         
50518         this.updateHeaderSortState();
50519
50520         this.beforeInitialResize();
50521         this.layout(true);
50522
50523         // two part rendering gives faster view to the user
50524         this.renderPhase2.defer(1, this);
50525     },
50526
50527     renderPhase2 : function(){
50528         // render the rows now
50529         this.refresh();
50530         if(this.grid.autoSizeColumns){
50531             this.autoSizeColumns();
50532         }
50533     },
50534
50535     beforeInitialResize : function(){
50536
50537     },
50538
50539     onColumnSplitterMoved : function(i, w){
50540         this.userResized = true;
50541         var cm = this.grid.colModel;
50542         cm.setColumnWidth(i, w, true);
50543         var cid = cm.getColumnId(i);
50544         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50545         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50546         this.updateSplitters();
50547         this.layout();
50548         this.grid.fireEvent("columnresize", i, w);
50549     },
50550
50551     syncRowHeights : function(startIndex, endIndex){
50552         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
50553             startIndex = startIndex || 0;
50554             var mrows = this.getBodyTable().rows;
50555             var lrows = this.getLockedTable().rows;
50556             var len = mrows.length-1;
50557             endIndex = Math.min(endIndex || len, len);
50558             for(var i = startIndex; i <= endIndex; i++){
50559                 var m = mrows[i], l = lrows[i];
50560                 var h = Math.max(m.offsetHeight, l.offsetHeight);
50561                 m.style.height = l.style.height = h + "px";
50562             }
50563         }
50564     },
50565
50566     layout : function(initialRender, is2ndPass){
50567         var g = this.grid;
50568         var auto = g.autoHeight;
50569         var scrollOffset = 16;
50570         var c = g.getGridEl(), cm = this.cm,
50571                 expandCol = g.autoExpandColumn,
50572                 gv = this;
50573         //c.beginMeasure();
50574
50575         if(!c.dom.offsetWidth){ // display:none?
50576             if(initialRender){
50577                 this.lockedWrap.show();
50578                 this.mainWrap.show();
50579             }
50580             return;
50581         }
50582
50583         var hasLock = this.cm.isLocked(0);
50584
50585         var tbh = this.headerPanel.getHeight();
50586         var bbh = this.footerPanel.getHeight();
50587
50588         if(auto){
50589             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
50590             var newHeight = ch + c.getBorderWidth("tb");
50591             if(g.maxHeight){
50592                 newHeight = Math.min(g.maxHeight, newHeight);
50593             }
50594             c.setHeight(newHeight);
50595         }
50596
50597         if(g.autoWidth){
50598             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
50599         }
50600
50601         var s = this.scroller;
50602
50603         var csize = c.getSize(true);
50604
50605         this.el.setSize(csize.width, csize.height);
50606
50607         this.headerPanel.setWidth(csize.width);
50608         this.footerPanel.setWidth(csize.width);
50609
50610         var hdHeight = this.mainHd.getHeight();
50611         var vw = csize.width;
50612         var vh = csize.height - (tbh + bbh);
50613
50614         s.setSize(vw, vh);
50615
50616         var bt = this.getBodyTable();
50617         var ltWidth = hasLock ?
50618                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50619
50620         var scrollHeight = bt.offsetHeight;
50621         var scrollWidth = ltWidth + bt.offsetWidth;
50622         var vscroll = false, hscroll = false;
50623
50624         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50625
50626         var lw = this.lockedWrap, mw = this.mainWrap;
50627         var lb = this.lockedBody, mb = this.mainBody;
50628
50629         setTimeout(function(){
50630             var t = s.dom.offsetTop;
50631             var w = s.dom.clientWidth,
50632                 h = s.dom.clientHeight;
50633
50634             lw.setTop(t);
50635             lw.setSize(ltWidth, h);
50636
50637             mw.setLeftTop(ltWidth, t);
50638             mw.setSize(w-ltWidth, h);
50639
50640             lb.setHeight(h-hdHeight);
50641             mb.setHeight(h-hdHeight);
50642
50643             if(is2ndPass !== true && !gv.userResized && expandCol){
50644                 // high speed resize without full column calculation
50645                 
50646                 var ci = cm.getIndexById(expandCol);
50647                 if (ci < 0) {
50648                     ci = cm.findColumnIndex(expandCol);
50649                 }
50650                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50651                 var expandId = cm.getColumnId(ci);
50652                 var  tw = cm.getTotalWidth(false);
50653                 var currentWidth = cm.getColumnWidth(ci);
50654                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50655                 if(currentWidth != cw){
50656                     cm.setColumnWidth(ci, cw, true);
50657                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50658                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50659                     gv.updateSplitters();
50660                     gv.layout(false, true);
50661                 }
50662             }
50663
50664             if(initialRender){
50665                 lw.show();
50666                 mw.show();
50667             }
50668             //c.endMeasure();
50669         }, 10);
50670     },
50671
50672     onWindowResize : function(){
50673         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50674             return;
50675         }
50676         this.layout();
50677     },
50678
50679     appendFooter : function(parentEl){
50680         return null;
50681     },
50682
50683     sortAscText : "Sort Ascending",
50684     sortDescText : "Sort Descending",
50685     lockText : "Lock Column",
50686     unlockText : "Unlock Column",
50687     columnsText : "Columns"
50688 });
50689
50690
50691 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50692     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50693     this.proxy.el.addClass('x-grid3-col-dd');
50694 };
50695
50696 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50697     handleMouseDown : function(e){
50698
50699     },
50700
50701     callHandleMouseDown : function(e){
50702         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50703     }
50704 });
50705 /*
50706  * Based on:
50707  * Ext JS Library 1.1.1
50708  * Copyright(c) 2006-2007, Ext JS, LLC.
50709  *
50710  * Originally Released Under LGPL - original licence link has changed is not relivant.
50711  *
50712  * Fork - LGPL
50713  * <script type="text/javascript">
50714  */
50715  
50716 // private
50717 // This is a support class used internally by the Grid components
50718 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50719     this.grid = grid;
50720     this.view = grid.getView();
50721     this.proxy = this.view.resizeProxy;
50722     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50723         "gridSplitters" + this.grid.getGridEl().id, {
50724         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50725     });
50726     this.setHandleElId(Roo.id(hd));
50727     this.setOuterHandleElId(Roo.id(hd2));
50728     this.scroll = false;
50729 };
50730 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50731     fly: Roo.Element.fly,
50732
50733     b4StartDrag : function(x, y){
50734         this.view.headersDisabled = true;
50735         this.proxy.setHeight(this.view.mainWrap.getHeight());
50736         var w = this.cm.getColumnWidth(this.cellIndex);
50737         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50738         this.resetConstraints();
50739         this.setXConstraint(minw, 1000);
50740         this.setYConstraint(0, 0);
50741         this.minX = x - minw;
50742         this.maxX = x + 1000;
50743         this.startPos = x;
50744         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50745     },
50746
50747
50748     handleMouseDown : function(e){
50749         ev = Roo.EventObject.setEvent(e);
50750         var t = this.fly(ev.getTarget());
50751         if(t.hasClass("x-grid-split")){
50752             this.cellIndex = this.view.getCellIndex(t.dom);
50753             this.split = t.dom;
50754             this.cm = this.grid.colModel;
50755             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50756                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50757             }
50758         }
50759     },
50760
50761     endDrag : function(e){
50762         this.view.headersDisabled = false;
50763         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50764         var diff = endX - this.startPos;
50765         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50766     },
50767
50768     autoOffset : function(){
50769         this.setDelta(0,0);
50770     }
50771 });/*
50772  * Based on:
50773  * Ext JS Library 1.1.1
50774  * Copyright(c) 2006-2007, Ext JS, LLC.
50775  *
50776  * Originally Released Under LGPL - original licence link has changed is not relivant.
50777  *
50778  * Fork - LGPL
50779  * <script type="text/javascript">
50780  */
50781  
50782 // private
50783 // This is a support class used internally by the Grid components
50784 Roo.grid.GridDragZone = function(grid, config){
50785     this.view = grid.getView();
50786     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50787     if(this.view.lockedBody){
50788         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50789         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50790     }
50791     this.scroll = false;
50792     this.grid = grid;
50793     this.ddel = document.createElement('div');
50794     this.ddel.className = 'x-grid-dd-wrap';
50795 };
50796
50797 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50798     ddGroup : "GridDD",
50799
50800     getDragData : function(e){
50801         var t = Roo.lib.Event.getTarget(e);
50802         var rowIndex = this.view.findRowIndex(t);
50803         if(rowIndex !== false){
50804             var sm = this.grid.selModel;
50805             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50806               //  sm.mouseDown(e, t);
50807             //}
50808             if (e.hasModifier()){
50809                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50810             }
50811             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50812         }
50813         return false;
50814     },
50815
50816     onInitDrag : function(e){
50817         var data = this.dragData;
50818         this.ddel.innerHTML = this.grid.getDragDropText();
50819         this.proxy.update(this.ddel);
50820         // fire start drag?
50821     },
50822
50823     afterRepair : function(){
50824         this.dragging = false;
50825     },
50826
50827     getRepairXY : function(e, data){
50828         return false;
50829     },
50830
50831     onEndDrag : function(data, e){
50832         // fire end drag?
50833     },
50834
50835     onValidDrop : function(dd, e, id){
50836         // fire drag drop?
50837         this.hideProxy();
50838     },
50839
50840     beforeInvalidDrop : function(e, id){
50841
50842     }
50843 });/*
50844  * Based on:
50845  * Ext JS Library 1.1.1
50846  * Copyright(c) 2006-2007, Ext JS, LLC.
50847  *
50848  * Originally Released Under LGPL - original licence link has changed is not relivant.
50849  *
50850  * Fork - LGPL
50851  * <script type="text/javascript">
50852  */
50853  
50854
50855 /**
50856  * @class Roo.grid.ColumnModel
50857  * @extends Roo.util.Observable
50858  * This is the default implementation of a ColumnModel used by the Grid. It defines
50859  * the columns in the grid.
50860  * <br>Usage:<br>
50861  <pre><code>
50862  var colModel = new Roo.grid.ColumnModel([
50863         {header: "Ticker", width: 60, sortable: true, locked: true},
50864         {header: "Company Name", width: 150, sortable: true},
50865         {header: "Market Cap.", width: 100, sortable: true},
50866         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50867         {header: "Employees", width: 100, sortable: true, resizable: false}
50868  ]);
50869  </code></pre>
50870  * <p>
50871  
50872  * The config options listed for this class are options which may appear in each
50873  * individual column definition.
50874  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50875  * @constructor
50876  * @param {Object} config An Array of column config objects. See this class's
50877  * config objects for details.
50878 */
50879 Roo.grid.ColumnModel = function(config){
50880         /**
50881      * The config passed into the constructor
50882      */
50883     this.config = config;
50884     this.lookup = {};
50885
50886     // if no id, create one
50887     // if the column does not have a dataIndex mapping,
50888     // map it to the order it is in the config
50889     for(var i = 0, len = config.length; i < len; i++){
50890         var c = config[i];
50891         if(typeof c.dataIndex == "undefined"){
50892             c.dataIndex = i;
50893         }
50894         if(typeof c.renderer == "string"){
50895             c.renderer = Roo.util.Format[c.renderer];
50896         }
50897         if(typeof c.id == "undefined"){
50898             c.id = Roo.id();
50899         }
50900         if(c.editor && c.editor.xtype){
50901             c.editor  = Roo.factory(c.editor, Roo.grid);
50902         }
50903         if(c.editor && c.editor.isFormField){
50904             c.editor = new Roo.grid.GridEditor(c.editor);
50905         }
50906         this.lookup[c.id] = c;
50907     }
50908
50909     /**
50910      * The width of columns which have no width specified (defaults to 100)
50911      * @type Number
50912      */
50913     this.defaultWidth = 100;
50914
50915     /**
50916      * Default sortable of columns which have no sortable specified (defaults to false)
50917      * @type Boolean
50918      */
50919     this.defaultSortable = false;
50920
50921     this.addEvents({
50922         /**
50923              * @event widthchange
50924              * Fires when the width of a column changes.
50925              * @param {ColumnModel} this
50926              * @param {Number} columnIndex The column index
50927              * @param {Number} newWidth The new width
50928              */
50929             "widthchange": true,
50930         /**
50931              * @event headerchange
50932              * Fires when the text of a header changes.
50933              * @param {ColumnModel} this
50934              * @param {Number} columnIndex The column index
50935              * @param {Number} newText The new header text
50936              */
50937             "headerchange": true,
50938         /**
50939              * @event hiddenchange
50940              * Fires when a column is hidden or "unhidden".
50941              * @param {ColumnModel} this
50942              * @param {Number} columnIndex The column index
50943              * @param {Boolean} hidden true if hidden, false otherwise
50944              */
50945             "hiddenchange": true,
50946             /**
50947          * @event columnmoved
50948          * Fires when a column is moved.
50949          * @param {ColumnModel} this
50950          * @param {Number} oldIndex
50951          * @param {Number} newIndex
50952          */
50953         "columnmoved" : true,
50954         /**
50955          * @event columlockchange
50956          * Fires when a column's locked state is changed
50957          * @param {ColumnModel} this
50958          * @param {Number} colIndex
50959          * @param {Boolean} locked true if locked
50960          */
50961         "columnlockchange" : true
50962     });
50963     Roo.grid.ColumnModel.superclass.constructor.call(this);
50964 };
50965 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50966     /**
50967      * @cfg {String} header The header text to display in the Grid view.
50968      */
50969     /**
50970      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50971      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50972      * specified, the column's index is used as an index into the Record's data Array.
50973      */
50974     /**
50975      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50976      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50977      */
50978     /**
50979      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50980      * Defaults to the value of the {@link #defaultSortable} property.
50981      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50982      */
50983     /**
50984      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50985      */
50986     /**
50987      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50988      */
50989     /**
50990      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50991      */
50992     /**
50993      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50994      */
50995     /**
50996      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50997      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50998      * default renderer uses the raw data value.
50999      */
51000        /**
51001      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
51002      */
51003     /**
51004      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
51005      */
51006
51007     /**
51008      * Returns the id of the column at the specified index.
51009      * @param {Number} index The column index
51010      * @return {String} the id
51011      */
51012     getColumnId : function(index){
51013         return this.config[index].id;
51014     },
51015
51016     /**
51017      * Returns the column for a specified id.
51018      * @param {String} id The column id
51019      * @return {Object} the column
51020      */
51021     getColumnById : function(id){
51022         return this.lookup[id];
51023     },
51024
51025     
51026     /**
51027      * Returns the column for a specified dataIndex.
51028      * @param {String} dataIndex The column dataIndex
51029      * @return {Object|Boolean} the column or false if not found
51030      */
51031     getColumnByDataIndex: function(dataIndex){
51032         var index = this.findColumnIndex(dataIndex);
51033         return index > -1 ? this.config[index] : false;
51034     },
51035     
51036     /**
51037      * Returns the index for a specified column id.
51038      * @param {String} id The column id
51039      * @return {Number} the index, or -1 if not found
51040      */
51041     getIndexById : function(id){
51042         for(var i = 0, len = this.config.length; i < len; i++){
51043             if(this.config[i].id == id){
51044                 return i;
51045             }
51046         }
51047         return -1;
51048     },
51049     
51050     /**
51051      * Returns the index for a specified column dataIndex.
51052      * @param {String} dataIndex The column dataIndex
51053      * @return {Number} the index, or -1 if not found
51054      */
51055     
51056     findColumnIndex : function(dataIndex){
51057         for(var i = 0, len = this.config.length; i < len; i++){
51058             if(this.config[i].dataIndex == dataIndex){
51059                 return i;
51060             }
51061         }
51062         return -1;
51063     },
51064     
51065     
51066     moveColumn : function(oldIndex, newIndex){
51067         var c = this.config[oldIndex];
51068         this.config.splice(oldIndex, 1);
51069         this.config.splice(newIndex, 0, c);
51070         this.dataMap = null;
51071         this.fireEvent("columnmoved", this, oldIndex, newIndex);
51072     },
51073
51074     isLocked : function(colIndex){
51075         return this.config[colIndex].locked === true;
51076     },
51077
51078     setLocked : function(colIndex, value, suppressEvent){
51079         if(this.isLocked(colIndex) == value){
51080             return;
51081         }
51082         this.config[colIndex].locked = value;
51083         if(!suppressEvent){
51084             this.fireEvent("columnlockchange", this, colIndex, value);
51085         }
51086     },
51087
51088     getTotalLockedWidth : function(){
51089         var totalWidth = 0;
51090         for(var i = 0; i < this.config.length; i++){
51091             if(this.isLocked(i) && !this.isHidden(i)){
51092                 this.totalWidth += this.getColumnWidth(i);
51093             }
51094         }
51095         return totalWidth;
51096     },
51097
51098     getLockedCount : function(){
51099         for(var i = 0, len = this.config.length; i < len; i++){
51100             if(!this.isLocked(i)){
51101                 return i;
51102             }
51103         }
51104     },
51105
51106     /**
51107      * Returns the number of columns.
51108      * @return {Number}
51109      */
51110     getColumnCount : function(visibleOnly){
51111         if(visibleOnly === true){
51112             var c = 0;
51113             for(var i = 0, len = this.config.length; i < len; i++){
51114                 if(!this.isHidden(i)){
51115                     c++;
51116                 }
51117             }
51118             return c;
51119         }
51120         return this.config.length;
51121     },
51122
51123     /**
51124      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
51125      * @param {Function} fn
51126      * @param {Object} scope (optional)
51127      * @return {Array} result
51128      */
51129     getColumnsBy : function(fn, scope){
51130         var r = [];
51131         for(var i = 0, len = this.config.length; i < len; i++){
51132             var c = this.config[i];
51133             if(fn.call(scope||this, c, i) === true){
51134                 r[r.length] = c;
51135             }
51136         }
51137         return r;
51138     },
51139
51140     /**
51141      * Returns true if the specified column is sortable.
51142      * @param {Number} col The column index
51143      * @return {Boolean}
51144      */
51145     isSortable : function(col){
51146         if(typeof this.config[col].sortable == "undefined"){
51147             return this.defaultSortable;
51148         }
51149         return this.config[col].sortable;
51150     },
51151
51152     /**
51153      * Returns the rendering (formatting) function defined for the column.
51154      * @param {Number} col The column index.
51155      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
51156      */
51157     getRenderer : function(col){
51158         if(!this.config[col].renderer){
51159             return Roo.grid.ColumnModel.defaultRenderer;
51160         }
51161         return this.config[col].renderer;
51162     },
51163
51164     /**
51165      * Sets the rendering (formatting) function for a column.
51166      * @param {Number} col The column index
51167      * @param {Function} fn The function to use to process the cell's raw data
51168      * to return HTML markup for the grid view. The render function is called with
51169      * the following parameters:<ul>
51170      * <li>Data value.</li>
51171      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
51172      * <li>css A CSS style string to apply to the table cell.</li>
51173      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
51174      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
51175      * <li>Row index</li>
51176      * <li>Column index</li>
51177      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
51178      */
51179     setRenderer : function(col, fn){
51180         this.config[col].renderer = fn;
51181     },
51182
51183     /**
51184      * Returns the width for the specified column.
51185      * @param {Number} col The column index
51186      * @return {Number}
51187      */
51188     getColumnWidth : function(col){
51189         return this.config[col].width * 1 || this.defaultWidth;
51190     },
51191
51192     /**
51193      * Sets the width for a column.
51194      * @param {Number} col The column index
51195      * @param {Number} width The new width
51196      */
51197     setColumnWidth : function(col, width, suppressEvent){
51198         this.config[col].width = width;
51199         this.totalWidth = null;
51200         if(!suppressEvent){
51201              this.fireEvent("widthchange", this, col, width);
51202         }
51203     },
51204
51205     /**
51206      * Returns the total width of all columns.
51207      * @param {Boolean} includeHidden True to include hidden column widths
51208      * @return {Number}
51209      */
51210     getTotalWidth : function(includeHidden){
51211         if(!this.totalWidth){
51212             this.totalWidth = 0;
51213             for(var i = 0, len = this.config.length; i < len; i++){
51214                 if(includeHidden || !this.isHidden(i)){
51215                     this.totalWidth += this.getColumnWidth(i);
51216                 }
51217             }
51218         }
51219         return this.totalWidth;
51220     },
51221
51222     /**
51223      * Returns the header for the specified column.
51224      * @param {Number} col The column index
51225      * @return {String}
51226      */
51227     getColumnHeader : function(col){
51228         return this.config[col].header;
51229     },
51230
51231     /**
51232      * Sets the header for a column.
51233      * @param {Number} col The column index
51234      * @param {String} header The new header
51235      */
51236     setColumnHeader : function(col, header){
51237         this.config[col].header = header;
51238         this.fireEvent("headerchange", this, col, header);
51239     },
51240
51241     /**
51242      * Returns the tooltip for the specified column.
51243      * @param {Number} col The column index
51244      * @return {String}
51245      */
51246     getColumnTooltip : function(col){
51247             return this.config[col].tooltip;
51248     },
51249     /**
51250      * Sets the tooltip for a column.
51251      * @param {Number} col The column index
51252      * @param {String} tooltip The new tooltip
51253      */
51254     setColumnTooltip : function(col, tooltip){
51255             this.config[col].tooltip = tooltip;
51256     },
51257
51258     /**
51259      * Returns the dataIndex for the specified column.
51260      * @param {Number} col The column index
51261      * @return {Number}
51262      */
51263     getDataIndex : function(col){
51264         return this.config[col].dataIndex;
51265     },
51266
51267     /**
51268      * Sets the dataIndex for a column.
51269      * @param {Number} col The column index
51270      * @param {Number} dataIndex The new dataIndex
51271      */
51272     setDataIndex : function(col, dataIndex){
51273         this.config[col].dataIndex = dataIndex;
51274     },
51275
51276     
51277     
51278     /**
51279      * Returns true if the cell is editable.
51280      * @param {Number} colIndex The column index
51281      * @param {Number} rowIndex The row index
51282      * @return {Boolean}
51283      */
51284     isCellEditable : function(colIndex, rowIndex){
51285         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
51286     },
51287
51288     /**
51289      * Returns the editor defined for the cell/column.
51290      * return false or null to disable editing.
51291      * @param {Number} colIndex The column index
51292      * @param {Number} rowIndex The row index
51293      * @return {Object}
51294      */
51295     getCellEditor : function(colIndex, rowIndex){
51296         return this.config[colIndex].editor;
51297     },
51298
51299     /**
51300      * Sets if a column is editable.
51301      * @param {Number} col The column index
51302      * @param {Boolean} editable True if the column is editable
51303      */
51304     setEditable : function(col, editable){
51305         this.config[col].editable = editable;
51306     },
51307
51308
51309     /**
51310      * Returns true if the column is hidden.
51311      * @param {Number} colIndex The column index
51312      * @return {Boolean}
51313      */
51314     isHidden : function(colIndex){
51315         return this.config[colIndex].hidden;
51316     },
51317
51318
51319     /**
51320      * Returns true if the column width cannot be changed
51321      */
51322     isFixed : function(colIndex){
51323         return this.config[colIndex].fixed;
51324     },
51325
51326     /**
51327      * Returns true if the column can be resized
51328      * @return {Boolean}
51329      */
51330     isResizable : function(colIndex){
51331         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
51332     },
51333     /**
51334      * Sets if a column is hidden.
51335      * @param {Number} colIndex The column index
51336      * @param {Boolean} hidden True if the column is hidden
51337      */
51338     setHidden : function(colIndex, hidden){
51339         this.config[colIndex].hidden = hidden;
51340         this.totalWidth = null;
51341         this.fireEvent("hiddenchange", this, colIndex, hidden);
51342     },
51343
51344     /**
51345      * Sets the editor for a column.
51346      * @param {Number} col The column index
51347      * @param {Object} editor The editor object
51348      */
51349     setEditor : function(col, editor){
51350         this.config[col].editor = editor;
51351     }
51352 });
51353
51354 Roo.grid.ColumnModel.defaultRenderer = function(value){
51355         if(typeof value == "string" && value.length < 1){
51356             return "&#160;";
51357         }
51358         return value;
51359 };
51360
51361 // Alias for backwards compatibility
51362 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
51363 /*
51364  * Based on:
51365  * Ext JS Library 1.1.1
51366  * Copyright(c) 2006-2007, Ext JS, LLC.
51367  *
51368  * Originally Released Under LGPL - original licence link has changed is not relivant.
51369  *
51370  * Fork - LGPL
51371  * <script type="text/javascript">
51372  */
51373
51374 /**
51375  * @class Roo.grid.AbstractSelectionModel
51376  * @extends Roo.util.Observable
51377  * Abstract base class for grid SelectionModels.  It provides the interface that should be
51378  * implemented by descendant classes.  This class should not be directly instantiated.
51379  * @constructor
51380  */
51381 Roo.grid.AbstractSelectionModel = function(){
51382     this.locked = false;
51383     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
51384 };
51385
51386 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
51387     /** @ignore Called by the grid automatically. Do not call directly. */
51388     init : function(grid){
51389         this.grid = grid;
51390         this.initEvents();
51391     },
51392
51393     /**
51394      * Locks the selections.
51395      */
51396     lock : function(){
51397         this.locked = true;
51398     },
51399
51400     /**
51401      * Unlocks the selections.
51402      */
51403     unlock : function(){
51404         this.locked = false;
51405     },
51406
51407     /**
51408      * Returns true if the selections are locked.
51409      * @return {Boolean}
51410      */
51411     isLocked : function(){
51412         return this.locked;
51413     }
51414 });/*
51415  * Based on:
51416  * Ext JS Library 1.1.1
51417  * Copyright(c) 2006-2007, Ext JS, LLC.
51418  *
51419  * Originally Released Under LGPL - original licence link has changed is not relivant.
51420  *
51421  * Fork - LGPL
51422  * <script type="text/javascript">
51423  */
51424 /**
51425  * @extends Roo.grid.AbstractSelectionModel
51426  * @class Roo.grid.RowSelectionModel
51427  * The default SelectionModel used by {@link Roo.grid.Grid}.
51428  * It supports multiple selections and keyboard selection/navigation. 
51429  * @constructor
51430  * @param {Object} config
51431  */
51432 Roo.grid.RowSelectionModel = function(config){
51433     Roo.apply(this, config);
51434     this.selections = new Roo.util.MixedCollection(false, function(o){
51435         return o.id;
51436     });
51437
51438     this.last = false;
51439     this.lastActive = false;
51440
51441     this.addEvents({
51442         /**
51443              * @event selectionchange
51444              * Fires when the selection changes
51445              * @param {SelectionModel} this
51446              */
51447             "selectionchange" : true,
51448         /**
51449              * @event afterselectionchange
51450              * Fires after the selection changes (eg. by key press or clicking)
51451              * @param {SelectionModel} this
51452              */
51453             "afterselectionchange" : true,
51454         /**
51455              * @event beforerowselect
51456              * Fires when a row is selected being selected, return false to cancel.
51457              * @param {SelectionModel} this
51458              * @param {Number} rowIndex The selected index
51459              * @param {Boolean} keepExisting False if other selections will be cleared
51460              */
51461             "beforerowselect" : true,
51462         /**
51463              * @event rowselect
51464              * Fires when a row is selected.
51465              * @param {SelectionModel} this
51466              * @param {Number} rowIndex The selected index
51467              * @param {Roo.data.Record} r The record
51468              */
51469             "rowselect" : true,
51470         /**
51471              * @event rowdeselect
51472              * Fires when a row is deselected.
51473              * @param {SelectionModel} this
51474              * @param {Number} rowIndex The selected index
51475              */
51476         "rowdeselect" : true
51477     });
51478     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
51479     this.locked = false;
51480 };
51481
51482 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
51483     /**
51484      * @cfg {Boolean} singleSelect
51485      * True to allow selection of only one row at a time (defaults to false)
51486      */
51487     singleSelect : false,
51488
51489     // private
51490     initEvents : function(){
51491
51492         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
51493             this.grid.on("mousedown", this.handleMouseDown, this);
51494         }else{ // allow click to work like normal
51495             this.grid.on("rowclick", this.handleDragableRowClick, this);
51496         }
51497
51498         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
51499             "up" : function(e){
51500                 if(!e.shiftKey){
51501                     this.selectPrevious(e.shiftKey);
51502                 }else if(this.last !== false && this.lastActive !== false){
51503                     var last = this.last;
51504                     this.selectRange(this.last,  this.lastActive-1);
51505                     this.grid.getView().focusRow(this.lastActive);
51506                     if(last !== false){
51507                         this.last = last;
51508                     }
51509                 }else{
51510                     this.selectFirstRow();
51511                 }
51512                 this.fireEvent("afterselectionchange", this);
51513             },
51514             "down" : function(e){
51515                 if(!e.shiftKey){
51516                     this.selectNext(e.shiftKey);
51517                 }else if(this.last !== false && this.lastActive !== false){
51518                     var last = this.last;
51519                     this.selectRange(this.last,  this.lastActive+1);
51520                     this.grid.getView().focusRow(this.lastActive);
51521                     if(last !== false){
51522                         this.last = last;
51523                     }
51524                 }else{
51525                     this.selectFirstRow();
51526                 }
51527                 this.fireEvent("afterselectionchange", this);
51528             },
51529             scope: this
51530         });
51531
51532         var view = this.grid.view;
51533         view.on("refresh", this.onRefresh, this);
51534         view.on("rowupdated", this.onRowUpdated, this);
51535         view.on("rowremoved", this.onRemove, this);
51536     },
51537
51538     // private
51539     onRefresh : function(){
51540         var ds = this.grid.dataSource, i, v = this.grid.view;
51541         var s = this.selections;
51542         s.each(function(r){
51543             if((i = ds.indexOfId(r.id)) != -1){
51544                 v.onRowSelect(i);
51545             }else{
51546                 s.remove(r);
51547             }
51548         });
51549     },
51550
51551     // private
51552     onRemove : function(v, index, r){
51553         this.selections.remove(r);
51554     },
51555
51556     // private
51557     onRowUpdated : function(v, index, r){
51558         if(this.isSelected(r)){
51559             v.onRowSelect(index);
51560         }
51561     },
51562
51563     /**
51564      * Select records.
51565      * @param {Array} records The records to select
51566      * @param {Boolean} keepExisting (optional) True to keep existing selections
51567      */
51568     selectRecords : function(records, keepExisting){
51569         if(!keepExisting){
51570             this.clearSelections();
51571         }
51572         var ds = this.grid.dataSource;
51573         for(var i = 0, len = records.length; i < len; i++){
51574             this.selectRow(ds.indexOf(records[i]), true);
51575         }
51576     },
51577
51578     /**
51579      * Gets the number of selected rows.
51580      * @return {Number}
51581      */
51582     getCount : function(){
51583         return this.selections.length;
51584     },
51585
51586     /**
51587      * Selects the first row in the grid.
51588      */
51589     selectFirstRow : function(){
51590         this.selectRow(0);
51591     },
51592
51593     /**
51594      * Select the last row.
51595      * @param {Boolean} keepExisting (optional) True to keep existing selections
51596      */
51597     selectLastRow : function(keepExisting){
51598         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
51599     },
51600
51601     /**
51602      * Selects the row immediately following the last selected row.
51603      * @param {Boolean} keepExisting (optional) True to keep existing selections
51604      */
51605     selectNext : function(keepExisting){
51606         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
51607             this.selectRow(this.last+1, keepExisting);
51608             this.grid.getView().focusRow(this.last);
51609         }
51610     },
51611
51612     /**
51613      * Selects the row that precedes the last selected row.
51614      * @param {Boolean} keepExisting (optional) True to keep existing selections
51615      */
51616     selectPrevious : function(keepExisting){
51617         if(this.last){
51618             this.selectRow(this.last-1, keepExisting);
51619             this.grid.getView().focusRow(this.last);
51620         }
51621     },
51622
51623     /**
51624      * Returns the selected records
51625      * @return {Array} Array of selected records
51626      */
51627     getSelections : function(){
51628         return [].concat(this.selections.items);
51629     },
51630
51631     /**
51632      * Returns the first selected record.
51633      * @return {Record}
51634      */
51635     getSelected : function(){
51636         return this.selections.itemAt(0);
51637     },
51638
51639
51640     /**
51641      * Clears all selections.
51642      */
51643     clearSelections : function(fast){
51644         if(this.locked) return;
51645         if(fast !== true){
51646             var ds = this.grid.dataSource;
51647             var s = this.selections;
51648             s.each(function(r){
51649                 this.deselectRow(ds.indexOfId(r.id));
51650             }, this);
51651             s.clear();
51652         }else{
51653             this.selections.clear();
51654         }
51655         this.last = false;
51656     },
51657
51658
51659     /**
51660      * Selects all rows.
51661      */
51662     selectAll : function(){
51663         if(this.locked) return;
51664         this.selections.clear();
51665         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51666             this.selectRow(i, true);
51667         }
51668     },
51669
51670     /**
51671      * Returns True if there is a selection.
51672      * @return {Boolean}
51673      */
51674     hasSelection : function(){
51675         return this.selections.length > 0;
51676     },
51677
51678     /**
51679      * Returns True if the specified row is selected.
51680      * @param {Number/Record} record The record or index of the record to check
51681      * @return {Boolean}
51682      */
51683     isSelected : function(index){
51684         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51685         return (r && this.selections.key(r.id) ? true : false);
51686     },
51687
51688     /**
51689      * Returns True if the specified record id is selected.
51690      * @param {String} id The id of record to check
51691      * @return {Boolean}
51692      */
51693     isIdSelected : function(id){
51694         return (this.selections.key(id) ? true : false);
51695     },
51696
51697     // private
51698     handleMouseDown : function(e, t){
51699         var view = this.grid.getView(), rowIndex;
51700         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51701             return;
51702         };
51703         if(e.shiftKey && this.last !== false){
51704             var last = this.last;
51705             this.selectRange(last, rowIndex, e.ctrlKey);
51706             this.last = last; // reset the last
51707             view.focusRow(rowIndex);
51708         }else{
51709             var isSelected = this.isSelected(rowIndex);
51710             if(e.button !== 0 && isSelected){
51711                 view.focusRow(rowIndex);
51712             }else if(e.ctrlKey && isSelected){
51713                 this.deselectRow(rowIndex);
51714             }else if(!isSelected){
51715                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51716                 view.focusRow(rowIndex);
51717             }
51718         }
51719         this.fireEvent("afterselectionchange", this);
51720     },
51721     // private
51722     handleDragableRowClick :  function(grid, rowIndex, e) 
51723     {
51724         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51725             this.selectRow(rowIndex, false);
51726             grid.view.focusRow(rowIndex);
51727              this.fireEvent("afterselectionchange", this);
51728         }
51729     },
51730     
51731     /**
51732      * Selects multiple rows.
51733      * @param {Array} rows Array of the indexes of the row to select
51734      * @param {Boolean} keepExisting (optional) True to keep existing selections
51735      */
51736     selectRows : function(rows, keepExisting){
51737         if(!keepExisting){
51738             this.clearSelections();
51739         }
51740         for(var i = 0, len = rows.length; i < len; i++){
51741             this.selectRow(rows[i], true);
51742         }
51743     },
51744
51745     /**
51746      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51747      * @param {Number} startRow The index of the first row in the range
51748      * @param {Number} endRow The index of the last row in the range
51749      * @param {Boolean} keepExisting (optional) True to retain existing selections
51750      */
51751     selectRange : function(startRow, endRow, keepExisting){
51752         if(this.locked) return;
51753         if(!keepExisting){
51754             this.clearSelections();
51755         }
51756         if(startRow <= endRow){
51757             for(var i = startRow; i <= endRow; i++){
51758                 this.selectRow(i, true);
51759             }
51760         }else{
51761             for(var i = startRow; i >= endRow; i--){
51762                 this.selectRow(i, true);
51763             }
51764         }
51765     },
51766
51767     /**
51768      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51769      * @param {Number} startRow The index of the first row in the range
51770      * @param {Number} endRow The index of the last row in the range
51771      */
51772     deselectRange : function(startRow, endRow, preventViewNotify){
51773         if(this.locked) return;
51774         for(var i = startRow; i <= endRow; i++){
51775             this.deselectRow(i, preventViewNotify);
51776         }
51777     },
51778
51779     /**
51780      * Selects a row.
51781      * @param {Number} row The index of the row to select
51782      * @param {Boolean} keepExisting (optional) True to keep existing selections
51783      */
51784     selectRow : function(index, keepExisting, preventViewNotify){
51785         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51786         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51787             if(!keepExisting || this.singleSelect){
51788                 this.clearSelections();
51789             }
51790             var r = this.grid.dataSource.getAt(index);
51791             this.selections.add(r);
51792             this.last = this.lastActive = index;
51793             if(!preventViewNotify){
51794                 this.grid.getView().onRowSelect(index);
51795             }
51796             this.fireEvent("rowselect", this, index, r);
51797             this.fireEvent("selectionchange", this);
51798         }
51799     },
51800
51801     /**
51802      * Deselects a row.
51803      * @param {Number} row The index of the row to deselect
51804      */
51805     deselectRow : function(index, preventViewNotify){
51806         if(this.locked) return;
51807         if(this.last == index){
51808             this.last = false;
51809         }
51810         if(this.lastActive == index){
51811             this.lastActive = false;
51812         }
51813         var r = this.grid.dataSource.getAt(index);
51814         this.selections.remove(r);
51815         if(!preventViewNotify){
51816             this.grid.getView().onRowDeselect(index);
51817         }
51818         this.fireEvent("rowdeselect", this, index);
51819         this.fireEvent("selectionchange", this);
51820     },
51821
51822     // private
51823     restoreLast : function(){
51824         if(this._last){
51825             this.last = this._last;
51826         }
51827     },
51828
51829     // private
51830     acceptsNav : function(row, col, cm){
51831         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51832     },
51833
51834     // private
51835     onEditorKey : function(field, e){
51836         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51837         if(k == e.TAB){
51838             e.stopEvent();
51839             ed.completeEdit();
51840             if(e.shiftKey){
51841                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51842             }else{
51843                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51844             }
51845         }else if(k == e.ENTER && !e.ctrlKey){
51846             e.stopEvent();
51847             ed.completeEdit();
51848             if(e.shiftKey){
51849                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51850             }else{
51851                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51852             }
51853         }else if(k == e.ESC){
51854             ed.cancelEdit();
51855         }
51856         if(newCell){
51857             g.startEditing(newCell[0], newCell[1]);
51858         }
51859     }
51860 });/*
51861  * Based on:
51862  * Ext JS Library 1.1.1
51863  * Copyright(c) 2006-2007, Ext JS, LLC.
51864  *
51865  * Originally Released Under LGPL - original licence link has changed is not relivant.
51866  *
51867  * Fork - LGPL
51868  * <script type="text/javascript">
51869  */
51870 /**
51871  * @class Roo.grid.CellSelectionModel
51872  * @extends Roo.grid.AbstractSelectionModel
51873  * This class provides the basic implementation for cell selection in a grid.
51874  * @constructor
51875  * @param {Object} config The object containing the configuration of this model.
51876  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51877  */
51878 Roo.grid.CellSelectionModel = function(config){
51879     Roo.apply(this, config);
51880
51881     this.selection = null;
51882
51883     this.addEvents({
51884         /**
51885              * @event beforerowselect
51886              * Fires before a cell is selected.
51887              * @param {SelectionModel} this
51888              * @param {Number} rowIndex The selected row index
51889              * @param {Number} colIndex The selected cell index
51890              */
51891             "beforecellselect" : true,
51892         /**
51893              * @event cellselect
51894              * Fires when a cell is selected.
51895              * @param {SelectionModel} this
51896              * @param {Number} rowIndex The selected row index
51897              * @param {Number} colIndex The selected cell index
51898              */
51899             "cellselect" : true,
51900         /**
51901              * @event selectionchange
51902              * Fires when the active selection changes.
51903              * @param {SelectionModel} this
51904              * @param {Object} selection null for no selection or an object (o) with two properties
51905                 <ul>
51906                 <li>o.record: the record object for the row the selection is in</li>
51907                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51908                 </ul>
51909              */
51910             "selectionchange" : true,
51911         /**
51912              * @event tabend
51913              * Fires when the tab (or enter) was pressed on the last editable cell
51914              * You can use this to trigger add new row.
51915              * @param {SelectionModel} this
51916              */
51917             "tabend" : true,
51918          /**
51919              * @event beforeeditnext
51920              * Fires before the next editable sell is made active
51921              * You can use this to skip to another cell or fire the tabend
51922              *    if you set cell to false
51923              * @param {Object} eventdata object : { cell : [ row, col ] } 
51924              */
51925             "beforeeditnext" : true
51926     });
51927     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51928 };
51929
51930 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51931     
51932     enter_is_tab: false,
51933
51934     /** @ignore */
51935     initEvents : function(){
51936         this.grid.on("mousedown", this.handleMouseDown, this);
51937         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51938         var view = this.grid.view;
51939         view.on("refresh", this.onViewChange, this);
51940         view.on("rowupdated", this.onRowUpdated, this);
51941         view.on("beforerowremoved", this.clearSelections, this);
51942         view.on("beforerowsinserted", this.clearSelections, this);
51943         if(this.grid.isEditor){
51944             this.grid.on("beforeedit", this.beforeEdit,  this);
51945         }
51946     },
51947
51948         //private
51949     beforeEdit : function(e){
51950         this.select(e.row, e.column, false, true, e.record);
51951     },
51952
51953         //private
51954     onRowUpdated : function(v, index, r){
51955         if(this.selection && this.selection.record == r){
51956             v.onCellSelect(index, this.selection.cell[1]);
51957         }
51958     },
51959
51960         //private
51961     onViewChange : function(){
51962         this.clearSelections(true);
51963     },
51964
51965         /**
51966          * Returns the currently selected cell,.
51967          * @return {Array} The selected cell (row, column) or null if none selected.
51968          */
51969     getSelectedCell : function(){
51970         return this.selection ? this.selection.cell : null;
51971     },
51972
51973     /**
51974      * Clears all selections.
51975      * @param {Boolean} true to prevent the gridview from being notified about the change.
51976      */
51977     clearSelections : function(preventNotify){
51978         var s = this.selection;
51979         if(s){
51980             if(preventNotify !== true){
51981                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51982             }
51983             this.selection = null;
51984             this.fireEvent("selectionchange", this, null);
51985         }
51986     },
51987
51988     /**
51989      * Returns true if there is a selection.
51990      * @return {Boolean}
51991      */
51992     hasSelection : function(){
51993         return this.selection ? true : false;
51994     },
51995
51996     /** @ignore */
51997     handleMouseDown : function(e, t){
51998         var v = this.grid.getView();
51999         if(this.isLocked()){
52000             return;
52001         };
52002         var row = v.findRowIndex(t);
52003         var cell = v.findCellIndex(t);
52004         if(row !== false && cell !== false){
52005             this.select(row, cell);
52006         }
52007     },
52008
52009     /**
52010      * Selects a cell.
52011      * @param {Number} rowIndex
52012      * @param {Number} collIndex
52013      */
52014     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
52015         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
52016             this.clearSelections();
52017             r = r || this.grid.dataSource.getAt(rowIndex);
52018             this.selection = {
52019                 record : r,
52020                 cell : [rowIndex, colIndex]
52021             };
52022             if(!preventViewNotify){
52023                 var v = this.grid.getView();
52024                 v.onCellSelect(rowIndex, colIndex);
52025                 if(preventFocus !== true){
52026                     v.focusCell(rowIndex, colIndex);
52027                 }
52028             }
52029             this.fireEvent("cellselect", this, rowIndex, colIndex);
52030             this.fireEvent("selectionchange", this, this.selection);
52031         }
52032     },
52033
52034         //private
52035     isSelectable : function(rowIndex, colIndex, cm){
52036         return !cm.isHidden(colIndex);
52037     },
52038
52039     /** @ignore */
52040     handleKeyDown : function(e){
52041         //Roo.log('Cell Sel Model handleKeyDown');
52042         if(!e.isNavKeyPress()){
52043             return;
52044         }
52045         var g = this.grid, s = this.selection;
52046         if(!s){
52047             e.stopEvent();
52048             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
52049             if(cell){
52050                 this.select(cell[0], cell[1]);
52051             }
52052             return;
52053         }
52054         var sm = this;
52055         var walk = function(row, col, step){
52056             return g.walkCells(row, col, step, sm.isSelectable,  sm);
52057         };
52058         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
52059         var newCell;
52060
52061       
52062
52063         switch(k){
52064             case e.TAB:
52065                 // handled by onEditorKey
52066                 if (g.isEditor && g.editing) {
52067                     return;
52068                 }
52069                 if(e.shiftKey) {
52070                     newCell = walk(r, c-1, -1);
52071                 } else {
52072                     newCell = walk(r, c+1, 1);
52073                 }
52074                 break;
52075             
52076             case e.DOWN:
52077                newCell = walk(r+1, c, 1);
52078                 break;
52079             
52080             case e.UP:
52081                 newCell = walk(r-1, c, -1);
52082                 break;
52083             
52084             case e.RIGHT:
52085                 newCell = walk(r, c+1, 1);
52086                 break;
52087             
52088             case e.LEFT:
52089                 newCell = walk(r, c-1, -1);
52090                 break;
52091             
52092             case e.ENTER:
52093                 
52094                 if(g.isEditor && !g.editing){
52095                    g.startEditing(r, c);
52096                    e.stopEvent();
52097                    return;
52098                 }
52099                 
52100                 
52101              break;
52102         };
52103         if(newCell){
52104             this.select(newCell[0], newCell[1]);
52105             e.stopEvent();
52106             
52107         }
52108     },
52109
52110     acceptsNav : function(row, col, cm){
52111         return !cm.isHidden(col) && cm.isCellEditable(col, row);
52112     },
52113     /**
52114      * Selects a cell.
52115      * @param {Number} field (not used) - as it's normally used as a listener
52116      * @param {Number} e - event - fake it by using
52117      *
52118      * var e = Roo.EventObjectImpl.prototype;
52119      * e.keyCode = e.TAB
52120      *
52121      * 
52122      */
52123     onEditorKey : function(field, e){
52124         
52125         var k = e.getKey(),
52126             newCell,
52127             g = this.grid,
52128             ed = g.activeEditor,
52129             forward = false;
52130         ///Roo.log('onEditorKey' + k);
52131         
52132         
52133         if (this.enter_is_tab && k == e.ENTER) {
52134             k = e.TAB;
52135         }
52136         
52137         if(k == e.TAB){
52138             if(e.shiftKey){
52139                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
52140             }else{
52141                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52142                 forward = true;
52143             }
52144             
52145             e.stopEvent();
52146             
52147         } else if(k == e.ENTER &&  !e.ctrlKey){
52148             ed.completeEdit();
52149             e.stopEvent();
52150             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52151         
52152                 } else if(k == e.ESC){
52153             ed.cancelEdit();
52154         }
52155                 
52156         if (newCell) {
52157             var ecall = { cell : newCell, forward : forward };
52158             this.fireEvent('beforeeditnext', ecall );
52159             newCell = ecall.cell;
52160                         forward = ecall.forward;
52161         }
52162                 
52163         if(newCell){
52164             //Roo.log('next cell after edit');
52165             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
52166         } else if (forward) {
52167             // tabbed past last
52168             this.fireEvent.defer(100, this, ['tabend',this]);
52169         }
52170     }
52171 });/*
52172  * Based on:
52173  * Ext JS Library 1.1.1
52174  * Copyright(c) 2006-2007, Ext JS, LLC.
52175  *
52176  * Originally Released Under LGPL - original licence link has changed is not relivant.
52177  *
52178  * Fork - LGPL
52179  * <script type="text/javascript">
52180  */
52181  
52182 /**
52183  * @class Roo.grid.EditorGrid
52184  * @extends Roo.grid.Grid
52185  * Class for creating and editable grid.
52186  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
52187  * The container MUST have some type of size defined for the grid to fill. The container will be 
52188  * automatically set to position relative if it isn't already.
52189  * @param {Object} dataSource The data model to bind to
52190  * @param {Object} colModel The column model with info about this grid's columns
52191  */
52192 Roo.grid.EditorGrid = function(container, config){
52193     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
52194     this.getGridEl().addClass("xedit-grid");
52195
52196     if(!this.selModel){
52197         this.selModel = new Roo.grid.CellSelectionModel();
52198     }
52199
52200     this.activeEditor = null;
52201
52202         this.addEvents({
52203             /**
52204              * @event beforeedit
52205              * Fires before cell editing is triggered. The edit event object has the following properties <br />
52206              * <ul style="padding:5px;padding-left:16px;">
52207              * <li>grid - This grid</li>
52208              * <li>record - The record being edited</li>
52209              * <li>field - The field name being edited</li>
52210              * <li>value - The value for the field being edited.</li>
52211              * <li>row - The grid row index</li>
52212              * <li>column - The grid column index</li>
52213              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
52214              * </ul>
52215              * @param {Object} e An edit event (see above for description)
52216              */
52217             "beforeedit" : true,
52218             /**
52219              * @event afteredit
52220              * Fires after a cell is edited. <br />
52221              * <ul style="padding:5px;padding-left:16px;">
52222              * <li>grid - This grid</li>
52223              * <li>record - The record being edited</li>
52224              * <li>field - The field name being edited</li>
52225              * <li>value - The value being set</li>
52226              * <li>originalValue - The original value for the field, before the edit.</li>
52227              * <li>row - The grid row index</li>
52228              * <li>column - The grid column index</li>
52229              * </ul>
52230              * @param {Object} e An edit event (see above for description)
52231              */
52232             "afteredit" : true,
52233             /**
52234              * @event validateedit
52235              * Fires after a cell is edited, but before the value is set in the record. 
52236          * You can use this to modify the value being set in the field, Return false
52237              * to cancel the change. The edit event object has the following properties <br />
52238              * <ul style="padding:5px;padding-left:16px;">
52239          * <li>editor - This editor</li>
52240              * <li>grid - This grid</li>
52241              * <li>record - The record being edited</li>
52242              * <li>field - The field name being edited</li>
52243              * <li>value - The value being set</li>
52244              * <li>originalValue - The original value for the field, before the edit.</li>
52245              * <li>row - The grid row index</li>
52246              * <li>column - The grid column index</li>
52247              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
52248              * </ul>
52249              * @param {Object} e An edit event (see above for description)
52250              */
52251             "validateedit" : true
52252         });
52253     this.on("bodyscroll", this.stopEditing,  this);
52254     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
52255 };
52256
52257 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
52258     /**
52259      * @cfg {Number} clicksToEdit
52260      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
52261      */
52262     clicksToEdit: 2,
52263
52264     // private
52265     isEditor : true,
52266     // private
52267     trackMouseOver: false, // causes very odd FF errors
52268
52269     onCellDblClick : function(g, row, col){
52270         this.startEditing(row, col);
52271     },
52272
52273     onEditComplete : function(ed, value, startValue){
52274         this.editing = false;
52275         this.activeEditor = null;
52276         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
52277         var r = ed.record;
52278         var field = this.colModel.getDataIndex(ed.col);
52279         var e = {
52280             grid: this,
52281             record: r,
52282             field: field,
52283             originalValue: startValue,
52284             value: value,
52285             row: ed.row,
52286             column: ed.col,
52287             cancel:false,
52288             editor: ed
52289         };
52290         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
52291         cell.show();
52292           
52293         if(String(value) !== String(startValue)){
52294             
52295             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
52296                 r.set(field, e.value);
52297                 // if we are dealing with a combo box..
52298                 // then we also set the 'name' colum to be the displayField
52299                 if (ed.field.displayField && ed.field.name) {
52300                     r.set(ed.field.name, ed.field.el.dom.value);
52301                 }
52302                 
52303                 delete e.cancel; //?? why!!!
52304                 this.fireEvent("afteredit", e);
52305             }
52306         } else {
52307             this.fireEvent("afteredit", e); // always fire it!
52308         }
52309         this.view.focusCell(ed.row, ed.col);
52310     },
52311
52312     /**
52313      * Starts editing the specified for the specified row/column
52314      * @param {Number} rowIndex
52315      * @param {Number} colIndex
52316      */
52317     startEditing : function(row, col){
52318         this.stopEditing();
52319         if(this.colModel.isCellEditable(col, row)){
52320             this.view.ensureVisible(row, col, true);
52321           
52322             var r = this.dataSource.getAt(row);
52323             var field = this.colModel.getDataIndex(col);
52324             var cell = Roo.get(this.view.getCell(row,col));
52325             var e = {
52326                 grid: this,
52327                 record: r,
52328                 field: field,
52329                 value: r.data[field],
52330                 row: row,
52331                 column: col,
52332                 cancel:false 
52333             };
52334             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
52335                 this.editing = true;
52336                 var ed = this.colModel.getCellEditor(col, row);
52337                 
52338                 if (!ed) {
52339                     return;
52340                 }
52341                 if(!ed.rendered){
52342                     ed.render(ed.parentEl || document.body);
52343                 }
52344                 ed.field.reset();
52345                
52346                 cell.hide();
52347                 
52348                 (function(){ // complex but required for focus issues in safari, ie and opera
52349                     ed.row = row;
52350                     ed.col = col;
52351                     ed.record = r;
52352                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
52353                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
52354                     this.activeEditor = ed;
52355                     var v = r.data[field];
52356                     ed.startEdit(this.view.getCell(row, col), v);
52357                     // combo's with 'displayField and name set
52358                     if (ed.field.displayField && ed.field.name) {
52359                         ed.field.el.dom.value = r.data[ed.field.name];
52360                     }
52361                     
52362                     
52363                 }).defer(50, this);
52364             }
52365         }
52366     },
52367         
52368     /**
52369      * Stops any active editing
52370      */
52371     stopEditing : function(){
52372         if(this.activeEditor){
52373             this.activeEditor.completeEdit();
52374         }
52375         this.activeEditor = null;
52376     }
52377 });/*
52378  * Based on:
52379  * Ext JS Library 1.1.1
52380  * Copyright(c) 2006-2007, Ext JS, LLC.
52381  *
52382  * Originally Released Under LGPL - original licence link has changed is not relivant.
52383  *
52384  * Fork - LGPL
52385  * <script type="text/javascript">
52386  */
52387
52388 // private - not really -- you end up using it !
52389 // This is a support class used internally by the Grid components
52390
52391 /**
52392  * @class Roo.grid.GridEditor
52393  * @extends Roo.Editor
52394  * Class for creating and editable grid elements.
52395  * @param {Object} config any settings (must include field)
52396  */
52397 Roo.grid.GridEditor = function(field, config){
52398     if (!config && field.field) {
52399         config = field;
52400         field = Roo.factory(config.field, Roo.form);
52401     }
52402     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
52403     field.monitorTab = false;
52404 };
52405
52406 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
52407     
52408     /**
52409      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
52410      */
52411     
52412     alignment: "tl-tl",
52413     autoSize: "width",
52414     hideEl : false,
52415     cls: "x-small-editor x-grid-editor",
52416     shim:false,
52417     shadow:"frame"
52418 });/*
52419  * Based on:
52420  * Ext JS Library 1.1.1
52421  * Copyright(c) 2006-2007, Ext JS, LLC.
52422  *
52423  * Originally Released Under LGPL - original licence link has changed is not relivant.
52424  *
52425  * Fork - LGPL
52426  * <script type="text/javascript">
52427  */
52428   
52429
52430   
52431 Roo.grid.PropertyRecord = Roo.data.Record.create([
52432     {name:'name',type:'string'},  'value'
52433 ]);
52434
52435
52436 Roo.grid.PropertyStore = function(grid, source){
52437     this.grid = grid;
52438     this.store = new Roo.data.Store({
52439         recordType : Roo.grid.PropertyRecord
52440     });
52441     this.store.on('update', this.onUpdate,  this);
52442     if(source){
52443         this.setSource(source);
52444     }
52445     Roo.grid.PropertyStore.superclass.constructor.call(this);
52446 };
52447
52448
52449
52450 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
52451     setSource : function(o){
52452         this.source = o;
52453         this.store.removeAll();
52454         var data = [];
52455         for(var k in o){
52456             if(this.isEditableValue(o[k])){
52457                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
52458             }
52459         }
52460         this.store.loadRecords({records: data}, {}, true);
52461     },
52462
52463     onUpdate : function(ds, record, type){
52464         if(type == Roo.data.Record.EDIT){
52465             var v = record.data['value'];
52466             var oldValue = record.modified['value'];
52467             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
52468                 this.source[record.id] = v;
52469                 record.commit();
52470                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
52471             }else{
52472                 record.reject();
52473             }
52474         }
52475     },
52476
52477     getProperty : function(row){
52478        return this.store.getAt(row);
52479     },
52480
52481     isEditableValue: function(val){
52482         if(val && val instanceof Date){
52483             return true;
52484         }else if(typeof val == 'object' || typeof val == 'function'){
52485             return false;
52486         }
52487         return true;
52488     },
52489
52490     setValue : function(prop, value){
52491         this.source[prop] = value;
52492         this.store.getById(prop).set('value', value);
52493     },
52494
52495     getSource : function(){
52496         return this.source;
52497     }
52498 });
52499
52500 Roo.grid.PropertyColumnModel = function(grid, store){
52501     this.grid = grid;
52502     var g = Roo.grid;
52503     g.PropertyColumnModel.superclass.constructor.call(this, [
52504         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
52505         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
52506     ]);
52507     this.store = store;
52508     this.bselect = Roo.DomHelper.append(document.body, {
52509         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
52510             {tag: 'option', value: 'true', html: 'true'},
52511             {tag: 'option', value: 'false', html: 'false'}
52512         ]
52513     });
52514     Roo.id(this.bselect);
52515     var f = Roo.form;
52516     this.editors = {
52517         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
52518         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
52519         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
52520         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
52521         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
52522     };
52523     this.renderCellDelegate = this.renderCell.createDelegate(this);
52524     this.renderPropDelegate = this.renderProp.createDelegate(this);
52525 };
52526
52527 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
52528     
52529     
52530     nameText : 'Name',
52531     valueText : 'Value',
52532     
52533     dateFormat : 'm/j/Y',
52534     
52535     
52536     renderDate : function(dateVal){
52537         return dateVal.dateFormat(this.dateFormat);
52538     },
52539
52540     renderBool : function(bVal){
52541         return bVal ? 'true' : 'false';
52542     },
52543
52544     isCellEditable : function(colIndex, rowIndex){
52545         return colIndex == 1;
52546     },
52547
52548     getRenderer : function(col){
52549         return col == 1 ?
52550             this.renderCellDelegate : this.renderPropDelegate;
52551     },
52552
52553     renderProp : function(v){
52554         return this.getPropertyName(v);
52555     },
52556
52557     renderCell : function(val){
52558         var rv = val;
52559         if(val instanceof Date){
52560             rv = this.renderDate(val);
52561         }else if(typeof val == 'boolean'){
52562             rv = this.renderBool(val);
52563         }
52564         return Roo.util.Format.htmlEncode(rv);
52565     },
52566
52567     getPropertyName : function(name){
52568         var pn = this.grid.propertyNames;
52569         return pn && pn[name] ? pn[name] : name;
52570     },
52571
52572     getCellEditor : function(colIndex, rowIndex){
52573         var p = this.store.getProperty(rowIndex);
52574         var n = p.data['name'], val = p.data['value'];
52575         
52576         if(typeof(this.grid.customEditors[n]) == 'string'){
52577             return this.editors[this.grid.customEditors[n]];
52578         }
52579         if(typeof(this.grid.customEditors[n]) != 'undefined'){
52580             return this.grid.customEditors[n];
52581         }
52582         if(val instanceof Date){
52583             return this.editors['date'];
52584         }else if(typeof val == 'number'){
52585             return this.editors['number'];
52586         }else if(typeof val == 'boolean'){
52587             return this.editors['boolean'];
52588         }else{
52589             return this.editors['string'];
52590         }
52591     }
52592 });
52593
52594 /**
52595  * @class Roo.grid.PropertyGrid
52596  * @extends Roo.grid.EditorGrid
52597  * This class represents the  interface of a component based property grid control.
52598  * <br><br>Usage:<pre><code>
52599  var grid = new Roo.grid.PropertyGrid("my-container-id", {
52600       
52601  });
52602  // set any options
52603  grid.render();
52604  * </code></pre>
52605   
52606  * @constructor
52607  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52608  * The container MUST have some type of size defined for the grid to fill. The container will be
52609  * automatically set to position relative if it isn't already.
52610  * @param {Object} config A config object that sets properties on this grid.
52611  */
52612 Roo.grid.PropertyGrid = function(container, config){
52613     config = config || {};
52614     var store = new Roo.grid.PropertyStore(this);
52615     this.store = store;
52616     var cm = new Roo.grid.PropertyColumnModel(this, store);
52617     store.store.sort('name', 'ASC');
52618     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
52619         ds: store.store,
52620         cm: cm,
52621         enableColLock:false,
52622         enableColumnMove:false,
52623         stripeRows:false,
52624         trackMouseOver: false,
52625         clicksToEdit:1
52626     }, config));
52627     this.getGridEl().addClass('x-props-grid');
52628     this.lastEditRow = null;
52629     this.on('columnresize', this.onColumnResize, this);
52630     this.addEvents({
52631          /**
52632              * @event beforepropertychange
52633              * Fires before a property changes (return false to stop?)
52634              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52635              * @param {String} id Record Id
52636              * @param {String} newval New Value
52637          * @param {String} oldval Old Value
52638              */
52639         "beforepropertychange": true,
52640         /**
52641              * @event propertychange
52642              * Fires after a property changes
52643              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52644              * @param {String} id Record Id
52645              * @param {String} newval New Value
52646          * @param {String} oldval Old Value
52647              */
52648         "propertychange": true
52649     });
52650     this.customEditors = this.customEditors || {};
52651 };
52652 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52653     
52654      /**
52655      * @cfg {Object} customEditors map of colnames=> custom editors.
52656      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52657      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52658      * false disables editing of the field.
52659          */
52660     
52661       /**
52662      * @cfg {Object} propertyNames map of property Names to their displayed value
52663          */
52664     
52665     render : function(){
52666         Roo.grid.PropertyGrid.superclass.render.call(this);
52667         this.autoSize.defer(100, this);
52668     },
52669
52670     autoSize : function(){
52671         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52672         if(this.view){
52673             this.view.fitColumns();
52674         }
52675     },
52676
52677     onColumnResize : function(){
52678         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52679         this.autoSize();
52680     },
52681     /**
52682      * Sets the data for the Grid
52683      * accepts a Key => Value object of all the elements avaiable.
52684      * @param {Object} data  to appear in grid.
52685      */
52686     setSource : function(source){
52687         this.store.setSource(source);
52688         //this.autoSize();
52689     },
52690     /**
52691      * Gets all the data from the grid.
52692      * @return {Object} data  data stored in grid
52693      */
52694     getSource : function(){
52695         return this.store.getSource();
52696     }
52697 });/*
52698  * Based on:
52699  * Ext JS Library 1.1.1
52700  * Copyright(c) 2006-2007, Ext JS, LLC.
52701  *
52702  * Originally Released Under LGPL - original licence link has changed is not relivant.
52703  *
52704  * Fork - LGPL
52705  * <script type="text/javascript">
52706  */
52707  
52708 /**
52709  * @class Roo.LoadMask
52710  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52711  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52712  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52713  * element's UpdateManager load indicator and will be destroyed after the initial load.
52714  * @constructor
52715  * Create a new LoadMask
52716  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52717  * @param {Object} config The config object
52718  */
52719 Roo.LoadMask = function(el, config){
52720     this.el = Roo.get(el);
52721     Roo.apply(this, config);
52722     if(this.store){
52723         this.store.on('beforeload', this.onBeforeLoad, this);
52724         this.store.on('load', this.onLoad, this);
52725         this.store.on('loadexception', this.onLoadException, this);
52726         this.removeMask = false;
52727     }else{
52728         var um = this.el.getUpdateManager();
52729         um.showLoadIndicator = false; // disable the default indicator
52730         um.on('beforeupdate', this.onBeforeLoad, this);
52731         um.on('update', this.onLoad, this);
52732         um.on('failure', this.onLoad, this);
52733         this.removeMask = true;
52734     }
52735 };
52736
52737 Roo.LoadMask.prototype = {
52738     /**
52739      * @cfg {Boolean} removeMask
52740      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52741      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52742      */
52743     /**
52744      * @cfg {String} msg
52745      * The text to display in a centered loading message box (defaults to 'Loading...')
52746      */
52747     msg : 'Loading...',
52748     /**
52749      * @cfg {String} msgCls
52750      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52751      */
52752     msgCls : 'x-mask-loading',
52753
52754     /**
52755      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52756      * @type Boolean
52757      */
52758     disabled: false,
52759
52760     /**
52761      * Disables the mask to prevent it from being displayed
52762      */
52763     disable : function(){
52764        this.disabled = true;
52765     },
52766
52767     /**
52768      * Enables the mask so that it can be displayed
52769      */
52770     enable : function(){
52771         this.disabled = false;
52772     },
52773     
52774     onLoadException : function()
52775     {
52776         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52777             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52778         }
52779         this.el.unmask(this.removeMask);
52780     },
52781     // private
52782     onLoad : function()
52783     {
52784         this.el.unmask(this.removeMask);
52785     },
52786
52787     // private
52788     onBeforeLoad : function(){
52789         if(!this.disabled){
52790             this.el.mask(this.msg, this.msgCls);
52791         }
52792     },
52793
52794     // private
52795     destroy : function(){
52796         if(this.store){
52797             this.store.un('beforeload', this.onBeforeLoad, this);
52798             this.store.un('load', this.onLoad, this);
52799             this.store.un('loadexception', this.onLoadException, this);
52800         }else{
52801             var um = this.el.getUpdateManager();
52802             um.un('beforeupdate', this.onBeforeLoad, this);
52803             um.un('update', this.onLoad, this);
52804             um.un('failure', this.onLoad, this);
52805         }
52806     }
52807 };/*
52808  * Based on:
52809  * Ext JS Library 1.1.1
52810  * Copyright(c) 2006-2007, Ext JS, LLC.
52811  *
52812  * Originally Released Under LGPL - original licence link has changed is not relivant.
52813  *
52814  * Fork - LGPL
52815  * <script type="text/javascript">
52816  */
52817
52818
52819 /**
52820  * @class Roo.XTemplate
52821  * @extends Roo.Template
52822  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
52823 <pre><code>
52824 var t = new Roo.XTemplate(
52825         '&lt;select name="{name}"&gt;',
52826                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
52827         '&lt;/select&gt;'
52828 );
52829  
52830 // then append, applying the master template values
52831  </code></pre>
52832  *
52833  * Supported features:
52834  *
52835  *  Tags:
52836
52837 <pre><code>
52838       {a_variable} - output encoded.
52839       {a_variable.format:("Y-m-d")} - call a method on the variable
52840       {a_variable:raw} - unencoded output
52841       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
52842       {a_variable:this.method_on_template(...)} - call a method on the template object.
52843  
52844 </code></pre>
52845  *  The tpl tag:
52846 <pre><code>
52847         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
52848         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
52849         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
52850         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
52851   
52852         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
52853         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
52854 </code></pre>
52855  *      
52856  */
52857 Roo.XTemplate = function()
52858 {
52859     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52860     if (this.html) {
52861         this.compile();
52862     }
52863 };
52864
52865
52866 Roo.extend(Roo.XTemplate, Roo.Template, {
52867
52868     /**
52869      * The various sub templates
52870      */
52871     tpls : false,
52872     /**
52873      *
52874      * basic tag replacing syntax
52875      * WORD:WORD()
52876      *
52877      * // you can fake an object call by doing this
52878      *  x.t:(test,tesT) 
52879      * 
52880      */
52881     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52882
52883     /**
52884      * compile the template
52885      *
52886      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
52887      *
52888      */
52889     compile: function()
52890     {
52891         var s = this.html;
52892      
52893         s = ['<tpl>', s, '</tpl>'].join('');
52894     
52895         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
52896             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
52897             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
52898             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
52899             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
52900             m,
52901             id     = 0,
52902             tpls   = [];
52903     
52904         while(true == !!(m = s.match(re))){
52905             var forMatch   = m[0].match(nameRe),
52906                 ifMatch   = m[0].match(ifRe),
52907                 execMatch   = m[0].match(execRe),
52908                 namedMatch   = m[0].match(namedRe),
52909                 
52910                 exp  = null, 
52911                 fn   = null,
52912                 exec = null,
52913                 name = forMatch && forMatch[1] ? forMatch[1] : '';
52914                 
52915             if (ifMatch) {
52916                 // if - puts fn into test..
52917                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
52918                 if(exp){
52919                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52920                 }
52921             }
52922             
52923             if (execMatch) {
52924                 // exec - calls a function... returns empty if true is  returned.
52925                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
52926                 if(exp){
52927                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52928                 }
52929             }
52930             
52931             
52932             if (name) {
52933                 // for = 
52934                 switch(name){
52935                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52936                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52937                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52938                 }
52939             }
52940             var uid = namedMatch ? namedMatch[1] : id;
52941             
52942             
52943             tpls.push({
52944                 id:     namedMatch ? namedMatch[1] : id,
52945                 target: name,
52946                 exec:   exec,
52947                 test:   fn,
52948                 body:   m[1] || ''
52949             });
52950             if (namedMatch) {
52951                 s = s.replace(m[0], '');
52952             } else { 
52953                 s = s.replace(m[0], '{xtpl'+ id + '}');
52954             }
52955             ++id;
52956         }
52957         this.tpls = [];
52958         for(var i = tpls.length-1; i >= 0; --i){
52959             this.compileTpl(tpls[i]);
52960             this.tpls[tpls[i].id] = tpls[i];
52961         }
52962         this.master = tpls[tpls.length-1];
52963         return this;
52964     },
52965     /**
52966      * same as applyTemplate, except it's done to one of the subTemplates
52967      * when using named templates, you can do:
52968      *
52969      * var str = pl.applySubTemplate('your-name', values);
52970      *
52971      * 
52972      * @param {Number} id of the template
52973      * @param {Object} values to apply to template
52974      * @param {Object} parent (normaly the instance of this object)
52975      */
52976     applySubTemplate : function(id, values, parent)
52977     {
52978         
52979         
52980         var t = this.tpls[id];
52981         
52982         
52983         try { 
52984             if(t.test && !t.test.call(this, values, parent)){
52985                 return '';
52986             }
52987         } catch(e) {
52988             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
52989             Roo.log(e.toString());
52990             Roo.log(t.test);
52991             return ''
52992         }
52993         try { 
52994             
52995             if(t.exec && t.exec.call(this, values, parent)){
52996                 return '';
52997             }
52998         } catch(e) {
52999             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
53000             Roo.log(e.toString());
53001             Roo.log(t.exec);
53002             return ''
53003         }
53004         try {
53005             var vs = t.target ? t.target.call(this, values, parent) : values;
53006             parent = t.target ? values : parent;
53007             if(t.target && vs instanceof Array){
53008                 var buf = [];
53009                 for(var i = 0, len = vs.length; i < len; i++){
53010                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
53011                 }
53012                 return buf.join('');
53013             }
53014             return t.compiled.call(this, vs, parent);
53015         } catch (e) {
53016             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
53017             Roo.log(e.toString());
53018             Roo.log(t.compiled);
53019             return '';
53020         }
53021     },
53022
53023     compileTpl : function(tpl)
53024     {
53025         var fm = Roo.util.Format;
53026         var useF = this.disableFormats !== true;
53027         var sep = Roo.isGecko ? "+" : ",";
53028         var undef = function(str) {
53029             Roo.log("Property not found :"  + str);
53030             return '';
53031         };
53032         
53033         var fn = function(m, name, format, args)
53034         {
53035             //Roo.log(arguments);
53036             args = args ? args.replace(/\\'/g,"'") : args;
53037             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
53038             if (typeof(format) == 'undefined') {
53039                 format= 'htmlEncode';
53040             }
53041             if (format == 'raw' ) {
53042                 format = false;
53043             }
53044             
53045             if(name.substr(0, 4) == 'xtpl'){
53046                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
53047             }
53048             
53049             // build an array of options to determine if value is undefined..
53050             
53051             // basically get 'xxxx.yyyy' then do
53052             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
53053             //    (function () { Roo.log("Property not found"); return ''; })() :
53054             //    ......
53055             
53056             var udef_ar = [];
53057             var lookfor = '';
53058             Roo.each(name.split('.'), function(st) {
53059                 lookfor += (lookfor.length ? '.': '') + st;
53060                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
53061             });
53062             
53063             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
53064             
53065             
53066             if(format && useF){
53067                 
53068                 args = args ? ',' + args : "";
53069                  
53070                 if(format.substr(0, 5) != "this."){
53071                     format = "fm." + format + '(';
53072                 }else{
53073                     format = 'this.call("'+ format.substr(5) + '", ';
53074                     args = ", values";
53075                 }
53076                 
53077                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
53078             }
53079              
53080             if (args.length) {
53081                 // called with xxyx.yuu:(test,test)
53082                 // change to ()
53083                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
53084             }
53085             // raw.. - :raw modifier..
53086             return "'"+ sep + udef_st  + name + ")"+sep+"'";
53087             
53088         };
53089         var body;
53090         // branched to use + in gecko and [].join() in others
53091         if(Roo.isGecko){
53092             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
53093                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
53094                     "';};};";
53095         }else{
53096             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
53097             body.push(tpl.body.replace(/(\r\n|\n)/g,
53098                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
53099             body.push("'].join('');};};");
53100             body = body.join('');
53101         }
53102         
53103         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
53104        
53105         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
53106         eval(body);
53107         
53108         return this;
53109     },
53110
53111     applyTemplate : function(values){
53112         return this.master.compiled.call(this, values, {});
53113         //var s = this.subs;
53114     },
53115
53116     apply : function(){
53117         return this.applyTemplate.apply(this, arguments);
53118     }
53119
53120  });
53121
53122 Roo.XTemplate.from = function(el){
53123     el = Roo.getDom(el);
53124     return new Roo.XTemplate(el.value || el.innerHTML);
53125 };/*
53126  * Original code for Roojs - LGPL
53127  * <script type="text/javascript">
53128  */
53129  
53130 /**
53131  * @class Roo.XComponent
53132  * A delayed Element creator...
53133  * Or a way to group chunks of interface together.
53134  * 
53135  * Mypart.xyx = new Roo.XComponent({
53136
53137     parent : 'Mypart.xyz', // empty == document.element.!!
53138     order : '001',
53139     name : 'xxxx'
53140     region : 'xxxx'
53141     disabled : function() {} 
53142      
53143     tree : function() { // return an tree of xtype declared components
53144         var MODULE = this;
53145         return 
53146         {
53147             xtype : 'NestedLayoutPanel',
53148             // technicall
53149         }
53150      ]
53151  *})
53152  *
53153  *
53154  * It can be used to build a big heiracy, with parent etc.
53155  * or you can just use this to render a single compoent to a dom element
53156  * MYPART.render(Roo.Element | String(id) | dom_element )
53157  * 
53158  * @extends Roo.util.Observable
53159  * @constructor
53160  * @param cfg {Object} configuration of component
53161  * 
53162  */
53163 Roo.XComponent = function(cfg) {
53164     Roo.apply(this, cfg);
53165     this.addEvents({ 
53166         /**
53167              * @event built
53168              * Fires when this the componnt is built
53169              * @param {Roo.XComponent} c the component
53170              */
53171         'built' : true
53172         
53173     });
53174     this.region = this.region || 'center'; // default..
53175     Roo.XComponent.register(this);
53176     this.modules = false;
53177     this.el = false; // where the layout goes..
53178     
53179     
53180 }
53181 Roo.extend(Roo.XComponent, Roo.util.Observable, {
53182     /**
53183      * @property el
53184      * The created element (with Roo.factory())
53185      * @type {Roo.Layout}
53186      */
53187     el  : false,
53188     
53189     /**
53190      * @property el
53191      * for BC  - use el in new code
53192      * @type {Roo.Layout}
53193      */
53194     panel : false,
53195     
53196     /**
53197      * @property layout
53198      * for BC  - use el in new code
53199      * @type {Roo.Layout}
53200      */
53201     layout : false,
53202     
53203      /**
53204      * @cfg {Function|boolean} disabled
53205      * If this module is disabled by some rule, return true from the funtion
53206      */
53207     disabled : false,
53208     
53209     /**
53210      * @cfg {String} parent 
53211      * Name of parent element which it get xtype added to..
53212      */
53213     parent: false,
53214     
53215     /**
53216      * @cfg {String} order
53217      * Used to set the order in which elements are created (usefull for multiple tabs)
53218      */
53219     
53220     order : false,
53221     /**
53222      * @cfg {String} name
53223      * String to display while loading.
53224      */
53225     name : false,
53226     /**
53227      * @cfg {String} region
53228      * Region to render component to (defaults to center)
53229      */
53230     region : 'center',
53231     
53232     /**
53233      * @cfg {Array} items
53234      * A single item array - the first element is the root of the tree..
53235      * It's done this way to stay compatible with the Xtype system...
53236      */
53237     items : false,
53238     
53239     /**
53240      * @property _tree
53241      * The method that retuns the tree of parts that make up this compoennt 
53242      * @type {function}
53243      */
53244     _tree  : false,
53245     
53246      /**
53247      * render
53248      * render element to dom or tree
53249      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
53250      */
53251     
53252     render : function(el)
53253     {
53254         
53255         el = el || false;
53256         var hp = this.parent ? 1 : 0;
53257         
53258         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
53259             // if parent is a '#.....' string, then let's use that..
53260             var ename = this.parent.substr(1)
53261             this.parent = false;
53262             el = Roo.get(ename);
53263             if (!el) {
53264                 Roo.log("Warning - element can not be found :#" + ename );
53265                 return;
53266             }
53267         }
53268         
53269         
53270         if (!this.parent) {
53271             
53272             el = el ? Roo.get(el) : false;      
53273             
53274             // it's a top level one..
53275             this.parent =  {
53276                 el : new Roo.BorderLayout(el || document.body, {
53277                 
53278                      center: {
53279                          titlebar: false,
53280                          autoScroll:false,
53281                          closeOnTab: true,
53282                          tabPosition: 'top',
53283                           //resizeTabs: true,
53284                          alwaysShowTabs: el && hp? false :  true,
53285                          hideTabs: el || !hp ? true :  false,
53286                          minTabWidth: 140
53287                      }
53288                  })
53289             }
53290         }
53291         
53292                 if (!this.parent.el) {
53293                         // probably an old style ctor, which has been disabled.
53294                         return;
53295                         
53296                 }
53297                 // The 'tree' method is  '_tree now' 
53298             
53299         var tree = this._tree ? this._tree() : this.tree();
53300         tree.region = tree.region || this.region;
53301         this.el = this.parent.el.addxtype(tree);
53302         this.fireEvent('built', this);
53303         
53304         this.panel = this.el;
53305         this.layout = this.panel.layout;
53306                 this.parentLayout = this.parent.layout  || false;  
53307          
53308     }
53309     
53310 });
53311
53312 Roo.apply(Roo.XComponent, {
53313     /**
53314      * @property  hideProgress
53315      * true to disable the building progress bar.. usefull on single page renders.
53316      * @type Boolean
53317      */
53318     hideProgress : false,
53319     /**
53320      * @property  buildCompleted
53321      * True when the builder has completed building the interface.
53322      * @type Boolean
53323      */
53324     buildCompleted : false,
53325      
53326     /**
53327      * @property  topModule
53328      * the upper most module - uses document.element as it's constructor.
53329      * @type Object
53330      */
53331      
53332     topModule  : false,
53333       
53334     /**
53335      * @property  modules
53336      * array of modules to be created by registration system.
53337      * @type {Array} of Roo.XComponent
53338      */
53339     
53340     modules : [],
53341     /**
53342      * @property  elmodules
53343      * array of modules to be created by which use #ID 
53344      * @type {Array} of Roo.XComponent
53345      */
53346      
53347     elmodules : [],
53348
53349     
53350     /**
53351      * Register components to be built later.
53352      *
53353      * This solves the following issues
53354      * - Building is not done on page load, but after an authentication process has occured.
53355      * - Interface elements are registered on page load
53356      * - Parent Interface elements may not be loaded before child, so this handles that..
53357      * 
53358      *
53359      * example:
53360      * 
53361      * MyApp.register({
53362           order : '000001',
53363           module : 'Pman.Tab.projectMgr',
53364           region : 'center',
53365           parent : 'Pman.layout',
53366           disabled : false,  // or use a function..
53367         })
53368      
53369      * * @param {Object} details about module
53370      */
53371     register : function(obj) {
53372                 
53373         Roo.XComponent.event.fireEvent('register', obj);
53374         switch(typeof(obj.disabled) ) {
53375                 
53376             case 'undefined':
53377                 break;
53378             
53379             case 'function':
53380                 if ( obj.disabled() ) {
53381                         return;
53382                 }
53383                 break;
53384             
53385             default:
53386                 if (obj.disabled) {
53387                         return;
53388                 }
53389                 break;
53390         }
53391                 
53392         this.modules.push(obj);
53393          
53394     },
53395     /**
53396      * convert a string to an object..
53397      * eg. 'AAA.BBB' -> finds AAA.BBB
53398
53399      */
53400     
53401     toObject : function(str)
53402     {
53403         if (!str || typeof(str) == 'object') {
53404             return str;
53405         }
53406         if (str.substring(0,1) == '#') {
53407             return str;
53408         }
53409
53410         var ar = str.split('.');
53411         var rt, o;
53412         rt = ar.shift();
53413             /** eval:var:o */
53414         try {
53415             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
53416         } catch (e) {
53417             throw "Module not found : " + str;
53418         }
53419         
53420         if (o === false) {
53421             throw "Module not found : " + str;
53422         }
53423         Roo.each(ar, function(e) {
53424             if (typeof(o[e]) == 'undefined') {
53425                 throw "Module not found : " + str;
53426             }
53427             o = o[e];
53428         });
53429         
53430         return o;
53431         
53432     },
53433     
53434     
53435     /**
53436      * move modules into their correct place in the tree..
53437      * 
53438      */
53439     preBuild : function ()
53440     {
53441         var _t = this;
53442         Roo.each(this.modules , function (obj)
53443         {
53444             Roo.XComponent.event.fireEvent('beforebuild', obj);
53445             
53446             var opar = obj.parent;
53447             try { 
53448                 obj.parent = this.toObject(opar);
53449             } catch(e) {
53450                 Roo.log("parent:toObject failed: " + e.toString());
53451                 return;
53452             }
53453             
53454             if (!obj.parent) {
53455                 Roo.debug && Roo.log("GOT top level module");
53456                 Roo.debug && Roo.log(obj);
53457                 obj.modules = new Roo.util.MixedCollection(false, 
53458                     function(o) { return o.order + '' }
53459                 );
53460                 this.topModule = obj;
53461                 return;
53462             }
53463                         // parent is a string (usually a dom element name..)
53464             if (typeof(obj.parent) == 'string') {
53465                 this.elmodules.push(obj);
53466                 return;
53467             }
53468             if (obj.parent.constructor != Roo.XComponent) {
53469                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
53470             }
53471             if (!obj.parent.modules) {
53472                 obj.parent.modules = new Roo.util.MixedCollection(false, 
53473                     function(o) { return o.order + '' }
53474                 );
53475             }
53476             if (obj.parent.disabled) {
53477                 obj.disabled = true;
53478             }
53479             obj.parent.modules.add(obj);
53480         }, this);
53481     },
53482     
53483      /**
53484      * make a list of modules to build.
53485      * @return {Array} list of modules. 
53486      */ 
53487     
53488     buildOrder : function()
53489     {
53490         var _this = this;
53491         var cmp = function(a,b) {   
53492             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
53493         };
53494         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
53495             throw "No top level modules to build";
53496         }
53497         
53498         // make a flat list in order of modules to build.
53499         var mods = this.topModule ? [ this.topModule ] : [];
53500                 
53501         // elmodules (is a list of DOM based modules )
53502         Roo.each(this.elmodules, function(e) {
53503             mods.push(e)
53504         });
53505
53506         
53507         // add modules to their parents..
53508         var addMod = function(m) {
53509             Roo.debug && Roo.log("build Order: add: " + m.name);
53510             
53511         mods.push(m);
53512         if (m.modules && !m.disabled) {
53513             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
53514             m.modules.keySort('ASC',  cmp );
53515             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
53516
53517             m.modules.each(addMod);
53518         } else {
53519             Roo.debug && Roo.log("build Order: no child modules");
53520             }
53521             // not sure if this is used any more..
53522             if (m.finalize) {
53523                 m.finalize.name = m.name + " (clean up) ";
53524                 mods.push(m.finalize);
53525             }
53526             
53527         }
53528         if (this.topModule) { 
53529             this.topModule.modules.keySort('ASC',  cmp );
53530             this.topModule.modules.each(addMod);
53531         }
53532         return mods;
53533     },
53534     
53535      /**
53536      * Build the registered modules.
53537      * @param {Object} parent element.
53538      * @param {Function} optional method to call after module has been added.
53539      * 
53540      */ 
53541    
53542     build : function() 
53543     {
53544         
53545         this.preBuild();
53546         var mods = this.buildOrder();
53547       
53548         //this.allmods = mods;
53549         //Roo.debug && Roo.log(mods);
53550         //return;
53551         if (!mods.length) { // should not happen
53552             throw "NO modules!!!";
53553         }
53554         
53555         
53556         var msg = "Building Interface...";
53557         // flash it up as modal - so we store the mask!?
53558         if (!this.hideProgress) {
53559             Roo.MessageBox.show({ title: 'loading' });
53560             Roo.MessageBox.show({
53561                title: "Please wait...",
53562                msg: msg,
53563                width:450,
53564                progress:true,
53565                closable:false,
53566                modal: false
53567               
53568             });
53569         }
53570         var total = mods.length;
53571         
53572         var _this = this;
53573         var progressRun = function() {
53574             if (!mods.length) {
53575                 Roo.debug && Roo.log('hide?');
53576                 if (!this.hideProgress) {
53577                     Roo.MessageBox.hide();
53578                 }
53579                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
53580                 
53581                 // THE END...
53582                 return false;   
53583             }
53584             
53585             var m = mods.shift();
53586             
53587             
53588             Roo.debug && Roo.log(m);
53589             // not sure if this is supported any more.. - modules that are are just function
53590             if (typeof(m) == 'function') { 
53591                 m.call(this);
53592                 return progressRun.defer(10, _this);
53593             } 
53594             
53595             
53596             msg = "Building Interface " + (total  - mods.length) + 
53597                     " of " + total + 
53598                     (m.name ? (' - ' + m.name) : '');
53599                         Roo.debug && Roo.log(msg);
53600             if (!this.hideProgress) { 
53601                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
53602             }
53603             
53604          
53605             // is the module disabled?
53606             var disabled = (typeof(m.disabled) == 'function') ?
53607                 m.disabled.call(m.module.disabled) : m.disabled;    
53608             
53609             
53610             if (disabled) {
53611                 return progressRun(); // we do not update the display!
53612             }
53613             
53614             // now build 
53615             
53616                         
53617                         
53618             m.render();
53619             // it's 10 on top level, and 1 on others??? why...
53620             return progressRun.defer(10, _this);
53621              
53622         }
53623         progressRun.defer(1, _this);
53624      
53625         
53626         
53627     },
53628         
53629         
53630         /**
53631          * Event Object.
53632          *
53633          *
53634          */
53635         event: false, 
53636     /**
53637          * wrapper for event.on - aliased later..  
53638          * Typically use to register a event handler for register:
53639          *
53640          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
53641          *
53642          */
53643     on : false
53644    
53645     
53646     
53647 });
53648
53649 Roo.XComponent.event = new Roo.util.Observable({
53650                 events : { 
53651                         /**
53652                          * @event register
53653                          * Fires when an Component is registered,
53654                          * set the disable property on the Component to stop registration.
53655                          * @param {Roo.XComponent} c the component being registerd.
53656                          * 
53657                          */
53658                         'register' : true,
53659             /**
53660                          * @event beforebuild
53661                          * Fires before each Component is built
53662                          * can be used to apply permissions.
53663                          * @param {Roo.XComponent} c the component being registerd.
53664                          * 
53665                          */
53666                         'beforebuild' : true,
53667                         /**
53668                          * @event buildcomplete
53669                          * Fires on the top level element when all elements have been built
53670                          * @param {Roo.XComponent} the top level component.
53671                          */
53672                         'buildcomplete' : true
53673                         
53674                 }
53675 });
53676
53677 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
53678  //<script type="text/javascript">
53679
53680
53681 /**
53682  * @class Roo.Login
53683  * @extends Roo.LayoutDialog
53684  * A generic Login Dialog..... - only one needed in theory!?!?
53685  *
53686  * Fires XComponent builder on success...
53687  * 
53688  * Sends 
53689  *    username,password, lang = for login actions.
53690  *    check = 1 for periodic checking that sesion is valid.
53691  *    passwordRequest = email request password
53692  *    logout = 1 = to logout
53693  * 
53694  * Affects: (this id="????" elements)
53695  *   loading  (removed) (used to indicate application is loading)
53696  *   loading-mask (hides) (used to hide application when it's building loading)
53697  *   
53698  * 
53699  * Usage: 
53700  *    
53701  * 
53702  * Myapp.login = Roo.Login({
53703      url: xxxx,
53704    
53705      realm : 'Myapp', 
53706      
53707      
53708      method : 'POST',
53709      
53710      
53711      * 
53712  })
53713  * 
53714  * 
53715  * 
53716  **/
53717  
53718 Roo.Login = function(cfg)
53719 {
53720     this.addEvents({
53721         'refreshed' : true
53722     });
53723     
53724     Roo.apply(this,cfg);
53725     
53726     Roo.onReady(function() {
53727         this.onLoad();
53728     }, this);
53729     // call parent..
53730     
53731    
53732     Roo.Login.superclass.constructor.call(this, this);
53733     //this.addxtype(this.items[0]);
53734     
53735     
53736 }
53737
53738
53739 Roo.extend(Roo.Login, Roo.LayoutDialog, {
53740     
53741     /**
53742      * @cfg {String} method
53743      * Method used to query for login details.
53744      */
53745     
53746     method : 'POST',
53747     /**
53748      * @cfg {String} url
53749      * URL to query login data. - eg. baseURL + '/Login.php'
53750      */
53751     url : '',
53752     
53753     /**
53754      * @property user
53755      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
53756      * @type {Object} 
53757      */
53758     user : false,
53759     /**
53760      * @property checkFails
53761      * Number of times we have attempted to get authentication check, and failed.
53762      * @type {Number} 
53763      */
53764     checkFails : 0,
53765       /**
53766      * @property intervalID
53767      * The window interval that does the constant login checking.
53768      * @type {Number} 
53769      */
53770     intervalID : 0,
53771     
53772     
53773     onLoad : function() // called on page load...
53774     {
53775         // load 
53776          
53777         if (Roo.get('loading')) { // clear any loading indicator..
53778             Roo.get('loading').remove();
53779         }
53780         
53781         //this.switchLang('en'); // set the language to english..
53782        
53783         this.check({
53784             success:  function(response, opts)  {  // check successfull...
53785             
53786                 var res = this.processResponse(response);
53787                 this.checkFails =0;
53788                 if (!res.success) { // error!
53789                     this.checkFails = 5;
53790                     //console.log('call failure');
53791                     return this.failure(response,opts);
53792                 }
53793                 
53794                 if (!res.data.id) { // id=0 == login failure.
53795                     return this.show();
53796                 }
53797                 
53798                               
53799                         //console.log(success);
53800                 this.fillAuth(res.data);   
53801                 this.checkFails =0;
53802                 Roo.XComponent.build();
53803             },
53804             failure : this.show
53805         });
53806         
53807     }, 
53808     
53809     
53810     check: function(cfg) // called every so often to refresh cookie etc..
53811     {
53812         if (cfg.again) { // could be undefined..
53813             this.checkFails++;
53814         } else {
53815             this.checkFails = 0;
53816         }
53817         var _this = this;
53818         if (this.sending) {
53819             if ( this.checkFails > 4) {
53820                 Roo.MessageBox.alert("Error",  
53821                     "Error getting authentication status. - try reloading, or wait a while", function() {
53822                         _this.sending = false;
53823                     }); 
53824                 return;
53825             }
53826             cfg.again = true;
53827             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
53828             return;
53829         }
53830         this.sending = true;
53831         
53832         Roo.Ajax.request({  
53833             url: this.url,
53834             params: {
53835                 getAuthUser: true
53836             },  
53837             method: this.method,
53838             success:  cfg.success || this.success,
53839             failure : cfg.failure || this.failure,
53840             scope : this,
53841             callCfg : cfg
53842               
53843         });  
53844     }, 
53845     
53846     
53847     logout: function()
53848     {
53849         window.onbeforeunload = function() { }; // false does not work for IE..
53850         this.user = false;
53851         var _this = this;
53852         
53853         Roo.Ajax.request({  
53854             url: this.url,
53855             params: {
53856                 logout: 1
53857             },  
53858             method: 'GET',
53859             failure : function() {
53860                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
53861                     document.location = document.location.toString() + '?ts=' + Math.random();
53862                 });
53863                 
53864             },
53865             success : function() {
53866                 _this.user = false;
53867                 this.checkFails =0;
53868                 // fixme..
53869                 document.location = document.location.toString() + '?ts=' + Math.random();
53870             }
53871               
53872               
53873         }); 
53874     },
53875     
53876     processResponse : function (response)
53877     {
53878         var res = '';
53879         try {
53880             res = Roo.decode(response.responseText);
53881             // oops...
53882             if (typeof(res) != 'object') {
53883                 res = { success : false, errorMsg : res, errors : true };
53884             }
53885             if (typeof(res.success) == 'undefined') {
53886                 res.success = false;
53887             }
53888             
53889         } catch(e) {
53890             res = { success : false,  errorMsg : response.responseText, errors : true };
53891         }
53892         return res;
53893     },
53894     
53895     success : function(response, opts)  // check successfull...
53896     {  
53897         this.sending = false;
53898         var res = this.processResponse(response);
53899         if (!res.success) {
53900             return this.failure(response, opts);
53901         }
53902         if (!res.data || !res.data.id) {
53903             return this.failure(response,opts);
53904         }
53905         //console.log(res);
53906         this.fillAuth(res.data);
53907         
53908         this.checkFails =0;
53909         
53910     },
53911     
53912     
53913     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53914     {
53915         this.authUser = -1;
53916         this.sending = false;
53917         var res = this.processResponse(response);
53918         //console.log(res);
53919         if ( this.checkFails > 2) {
53920         
53921             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53922                 "Error getting authentication status. - try reloading"); 
53923             return;
53924         }
53925         opts.callCfg.again = true;
53926         this.check.defer(1000, this, [ opts.callCfg ]);
53927         return;  
53928     },
53929     
53930     
53931     
53932     fillAuth: function(au) {
53933         this.startAuthCheck();
53934         this.authUserId = au.id;
53935         this.authUser = au;
53936         this.lastChecked = new Date();
53937         this.fireEvent('refreshed', au);
53938         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53939         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53940         au.lang = au.lang || 'en';
53941         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53942         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53943         this.switchLang(au.lang );
53944         
53945      
53946         // open system... - -on setyp..
53947         if (this.authUserId  < 0) {
53948             Roo.MessageBox.alert("Warning", 
53949                 "This is an open system - please set up a admin user with a password.");  
53950         }
53951          
53952         //Pman.onload(); // which should do nothing if it's a re-auth result...
53953         
53954              
53955     },
53956     
53957     startAuthCheck : function() // starter for timeout checking..
53958     {
53959         if (this.intervalID) { // timer already in place...
53960             return false;
53961         }
53962         var _this = this;
53963         this.intervalID =  window.setInterval(function() {
53964               _this.check(false);
53965             }, 120000); // every 120 secs = 2mins..
53966         
53967         
53968     },
53969          
53970     
53971     switchLang : function (lang) 
53972     {
53973         _T = typeof(_T) == 'undefined' ? false : _T;
53974           if (!_T || !lang.length) {
53975             return;
53976         }
53977         
53978         if (!_T && lang != 'en') {
53979             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53980             return;
53981         }
53982         
53983         if (typeof(_T.en) == 'undefined') {
53984             _T.en = {};
53985             Roo.apply(_T.en, _T);
53986         }
53987         
53988         if (typeof(_T[lang]) == 'undefined') {
53989             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53990             return;
53991         }
53992         
53993         
53994         Roo.apply(_T, _T[lang]);
53995         // just need to set the text values for everything...
53996         var _this = this;
53997         /* this will not work ...
53998         if (this.form) { 
53999             
54000                
54001             function formLabel(name, val) {
54002                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
54003             }
54004             
54005             formLabel('password', "Password"+':');
54006             formLabel('username', "Email Address"+':');
54007             formLabel('lang', "Language"+':');
54008             this.dialog.setTitle("Login");
54009             this.dialog.buttons[0].setText("Forgot Password");
54010             this.dialog.buttons[1].setText("Login");
54011         }
54012         */
54013         
54014         
54015     },
54016     
54017     
54018     title: "Login",
54019     modal: true,
54020     width:  350,
54021     //height: 230,
54022     height: 180,
54023     shadow: true,
54024     minWidth:200,
54025     minHeight:180,
54026     //proxyDrag: true,
54027     closable: false,
54028     draggable: false,
54029     collapsible: false,
54030     resizable: false,
54031     center: {  // needed??
54032         autoScroll:false,
54033         titlebar: false,
54034        // tabPosition: 'top',
54035         hideTabs: true,
54036         closeOnTab: true,
54037         alwaysShowTabs: false
54038     } ,
54039     listeners : {
54040         
54041         show  : function(dlg)
54042         {
54043             //console.log(this);
54044             this.form = this.layout.getRegion('center').activePanel.form;
54045             this.form.dialog = dlg;
54046             this.buttons[0].form = this.form;
54047             this.buttons[0].dialog = dlg;
54048             this.buttons[1].form = this.form;
54049             this.buttons[1].dialog = dlg;
54050            
54051            //this.resizeToLogo.defer(1000,this);
54052             // this is all related to resizing for logos..
54053             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
54054            //// if (!sz) {
54055              //   this.resizeToLogo.defer(1000,this);
54056              //   return;
54057            // }
54058             //var w = Ext.lib.Dom.getViewWidth() - 100;
54059             //var h = Ext.lib.Dom.getViewHeight() - 100;
54060             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
54061             //this.center();
54062             if (this.disabled) {
54063                 this.hide();
54064                 return;
54065             }
54066             
54067             if (this.user.id < 0) { // used for inital setup situations.
54068                 return;
54069             }
54070             
54071             if (this.intervalID) {
54072                 // remove the timer
54073                 window.clearInterval(this.intervalID);
54074                 this.intervalID = false;
54075             }
54076             
54077             
54078             if (Roo.get('loading')) {
54079                 Roo.get('loading').remove();
54080             }
54081             if (Roo.get('loading-mask')) {
54082                 Roo.get('loading-mask').hide();
54083             }
54084             
54085             //incomming._node = tnode;
54086             this.form.reset();
54087             //this.dialog.modal = !modal;
54088             //this.dialog.show();
54089             this.el.unmask(); 
54090             
54091             
54092             this.form.setValues({
54093                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
54094                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
54095             });
54096             
54097             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
54098             if (this.form.findField('username').getValue().length > 0 ){
54099                 this.form.findField('password').focus();
54100             } else {
54101                this.form.findField('username').focus();
54102             }
54103     
54104         }
54105     },
54106     items : [
54107          {
54108        
54109             xtype : 'ContentPanel',
54110             xns : Roo,
54111             region: 'center',
54112             fitToFrame : true,
54113             
54114             items : [
54115     
54116                 {
54117                
54118                     xtype : 'Form',
54119                     xns : Roo.form,
54120                     labelWidth: 100,
54121                     style : 'margin: 10px;',
54122                     
54123                     listeners : {
54124                         actionfailed : function(f, act) {
54125                             // form can return { errors: .... }
54126                                 
54127                             //act.result.errors // invalid form element list...
54128                             //act.result.errorMsg// invalid form element list...
54129                             
54130                             this.dialog.el.unmask();
54131                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
54132                                         "Login failed - communication error - try again.");
54133                                       
54134                         },
54135                         actioncomplete: function(re, act) {
54136                              
54137                             Roo.state.Manager.set(
54138                                 this.dialog.realm + '.username',  
54139                                     this.findField('username').getValue()
54140                             );
54141                             Roo.state.Manager.set(
54142                                 this.dialog.realm + '.lang',  
54143                                 this.findField('lang').getValue() 
54144                             );
54145                             
54146                             this.dialog.fillAuth(act.result.data);
54147                               
54148                             this.dialog.hide();
54149                             
54150                             if (Roo.get('loading-mask')) {
54151                                 Roo.get('loading-mask').show();
54152                             }
54153                             Roo.XComponent.build();
54154                             
54155                              
54156                             
54157                         }
54158                     },
54159                     items : [
54160                         {
54161                             xtype : 'TextField',
54162                             xns : Roo.form,
54163                             fieldLabel: "Email Address",
54164                             name: 'username',
54165                             width:200,
54166                             autoCreate : {tag: "input", type: "text", size: "20"}
54167                         },
54168                         {
54169                             xtype : 'TextField',
54170                             xns : Roo.form,
54171                             fieldLabel: "Password",
54172                             inputType: 'password',
54173                             name: 'password',
54174                             width:200,
54175                             autoCreate : {tag: "input", type: "text", size: "20"},
54176                             listeners : {
54177                                 specialkey : function(e,ev) {
54178                                     if (ev.keyCode == 13) {
54179                                         this.form.dialog.el.mask("Logging in");
54180                                         this.form.doAction('submit', {
54181                                             url: this.form.dialog.url,
54182                                             method: this.form.dialog.method
54183                                         });
54184                                     }
54185                                 }
54186                             }  
54187                         },
54188                         {
54189                             xtype : 'ComboBox',
54190                             xns : Roo.form,
54191                             fieldLabel: "Language",
54192                             name : 'langdisp',
54193                             store: {
54194                                 xtype : 'SimpleStore',
54195                                 fields: ['lang', 'ldisp'],
54196                                 data : [
54197                                     [ 'en', 'English' ],
54198                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
54199                                     [ 'zh_CN', '\u7C21\u4E2D' ]
54200                                 ]
54201                             },
54202                             
54203                             valueField : 'lang',
54204                             hiddenName:  'lang',
54205                             width: 200,
54206                             displayField:'ldisp',
54207                             typeAhead: false,
54208                             editable: false,
54209                             mode: 'local',
54210                             triggerAction: 'all',
54211                             emptyText:'Select a Language...',
54212                             selectOnFocus:true,
54213                             listeners : {
54214                                 select :  function(cb, rec, ix) {
54215                                     this.form.switchLang(rec.data.lang);
54216                                 }
54217                             }
54218                         
54219                         }
54220                     ]
54221                 }
54222                   
54223                 
54224             ]
54225         }
54226     ],
54227     buttons : [
54228         {
54229             xtype : 'Button',
54230             xns : 'Roo',
54231             text : "Forgot Password",
54232             listeners : {
54233                 click : function() {
54234                     //console.log(this);
54235                     var n = this.form.findField('username').getValue();
54236                     if (!n.length) {
54237                         Roo.MessageBox.alert("Error", "Fill in your email address");
54238                         return;
54239                     }
54240                     Roo.Ajax.request({
54241                         url: this.dialog.url,
54242                         params: {
54243                             passwordRequest: n
54244                         },
54245                         method: this.dialog.method,
54246                         success:  function(response, opts)  {  // check successfull...
54247                         
54248                             var res = this.dialog.processResponse(response);
54249                             if (!res.success) { // error!
54250                                Roo.MessageBox.alert("Error" ,
54251                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
54252                                return;
54253                             }
54254                             Roo.MessageBox.alert("Notice" ,
54255                                 "Please check you email for the Password Reset message");
54256                         },
54257                         failure : function() {
54258                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
54259                         }
54260                         
54261                     });
54262                 }
54263             }
54264         },
54265         {
54266             xtype : 'Button',
54267             xns : 'Roo',
54268             text : "Login",
54269             listeners : {
54270                 
54271                 click : function () {
54272                         
54273                     this.dialog.el.mask("Logging in");
54274                     this.form.doAction('submit', {
54275                             url: this.dialog.url,
54276                             method: this.dialog.method
54277                     });
54278                 }
54279             }
54280         }
54281     ]
54282   
54283   
54284 })
54285  
54286
54287
54288